From 268dea41b72dadb66e41a7930f682122e1b003f0 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 20 Sep 2019 13:26:40 -0700 Subject: [PATCH 01/27] Implement lambda discard parameters --- .../Binder/Binder.QueryUnboundLambdaState.cs | 1 + .../CSharp/Portable/Binder/Binder_Lambda.cs | 16 ++ .../Binder/WithLambdaParametersBinder.cs | 7 +- .../Portable/BoundTree/UnboundLambda.cs | 25 ++ .../Portable/CSharpResources.Designer.cs | 9 + .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/MessageID.cs | 5 + .../Portable/Symbols/Source/LambdaSymbol.cs | 6 +- .../Source/SourceSimpleParameterSymbol.cs | 35 ++- .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Semantics/LambdaDiscardParametersTests.cs | 219 ++++++++++++++++++ .../Test/Semantic/Semantics/LambdaTests.cs | 52 +++++ .../SyntacticClassifierTests_Preprocessor.cs | 18 ++ .../QuickInfo/SemanticQuickInfoSourceTests.cs | 28 +++ .../UseLocalFunction/UseLocalFunctionTests.cs | 22 ++ .../Portable/Traits/CompilerFeature.cs | 1 + 28 files changed, 508 insertions(+), 4 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index 149b961300be7..b516d18544ca1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -26,6 +26,7 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, _bodyFactory = bodyFactory; } + public override bool UnderscoreMeansDiscard { get { return false; } } public override string ParameterName(int index) { return _parameters[index].Name; } public override bool HasSignature { get { return true; } } public override bool HasExplicitlyTypedParameterList { get { return false; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index c2627f7db6501..58d1f9d29e566 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -235,6 +235,8 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB var binder = new LocalScopeBinder(this); bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions); var pNames = PooledHashSet.GetInstance(); + bool underscoreMeansDiscard = lambda.UnderscoreMeansDiscard; + bool seenDiscard = false; for (int i = 0; i < lambda.ParameterCount; i++) { @@ -245,6 +247,20 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB continue; } + if (name == "_" && underscoreMeansDiscard) + { + if (seenDiscard) + { + MessageID.IDS_FeatureLambdaDiscardParameters.CheckFeatureAvailability( + diagnostics, + binder.Compilation, + lambda.ParameterLocation(i)); + } + + seenDiscard = true; + continue; + } + if (!pNames.Add(name)) { // The parameter name '{0}' is a duplicate diff --git a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs index 4422e9b0a9142..cef52511fd004 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs @@ -28,7 +28,10 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) RecordDefinitions(parameters); foreach (var parameter in lambdaSymbol.Parameters) { - this.parameterMap.Add(parameter.Name, parameter); + if (!(parameter is IDiscardSymbol)) + { + this.parameterMap.Add(parameter.Name, parameter); + } } } } @@ -38,7 +41,7 @@ private void RecordDefinitions(ImmutableArray definitions) var declarationMap = _definitionMap ?? (_definitionMap = new SmallDictionary()); foreach (var s in definitions) { - if (!declarationMap.ContainsKey(s.Name)) + if (!(s is IDiscardSymbol) && !declarationMap.ContainsKey(s.Name)) { declarationMap.Add(s.Name, s); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 37c7dd19e6f14..35d396ab49157 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -376,6 +376,7 @@ public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTyp public TypeSymbol ParameterType(int index) { return ParameterTypeWithAnnotations(index).Type; } public Location ParameterLocation(int index) { return Data.ParameterLocation(index); } public string ParameterName(int index) { return Data.ParameterName(index); } + public bool UnderscoreMeansDiscard { get { return Data.UnderscoreMeansDiscard; } } } internal abstract class UnboundLambdaState @@ -415,6 +416,7 @@ public void SetUnboundLambda(UnboundLambda unbound) public abstract MessageID MessageID { get; } public abstract string ParameterName(int index); + public abstract bool UnderscoreMeansDiscard { get; } public abstract bool HasSignature { get; } public abstract bool HasExplicitlyTypedParameterList { get; } public abstract int ParameterCount { get; } @@ -1097,6 +1099,29 @@ internal PlainUnboundLambdaState( _isAsync = isAsync; } + public override bool UnderscoreMeansDiscard + { + get + { + bool foundOneUnderscore = false; + foreach (var name in _parameterNames) + { + if (name == "_") + { + if (foundOneUnderscore) + { + // found multiple underscores + return true; + } + + foundOneUnderscore = true; + } + } + + return false; + } + } + public override bool HasSignature { get { return !_parameterNames.IsDefault; } } public override bool HasExplicitlyTypedParameterList { get { return !_parameterTypesWithAnnotations.IsDefault; } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 800ff3a621dda..ce680305d3e29 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -11455,6 +11455,15 @@ internal static string IDS_FeatureLambda { } } + /// + /// Looks up a localized string similar to lambda discard parameters. + /// + internal static string IDS_FeatureLambdaDiscardParameters { + get { + return ResourceManager.GetString("IDS_FeatureLambdaDiscardParameters", resourceCulture); + } + } + /// /// Looks up a localized string similar to leading digit separator. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 72b43f1ab738e..87d4118af63eb 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5744,6 +5744,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ name shadowing in nested functions + + lambda discard parameters + Cannot use a collection of dynamic type in an asynchronous foreach diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 2c5b7633083c4..890829783d39c 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -182,6 +182,7 @@ internal enum MessageID IDS_FeatureNestedStackalloc = MessageBase + 12762, IDS_FeatureSwitchExpression = MessageBase + 12763, IDS_FeatureAsyncUsing = MessageBase + 12764, + IDS_FeatureLambdaDiscardParameters = MessageBase + 12765, } // Message IDs may refer to strings that need to be localized. @@ -289,6 +290,10 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // Checks are in the LanguageParser unless otherwise noted. switch (feature) { + // Preview features. + case MessageID.IDS_FeatureLambdaDiscardParameters: + return LanguageVersion.Preview; + // C# 8.0 features. case MessageID.IDS_FeatureAltInterpolatedVerbatimStrings: case MessageID.IDS_FeatureCoalesceAssignmentExpression: diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 739f70a539153..7fd029fb249f2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -332,6 +332,7 @@ private ImmutableArray MakeParameters( var hasExplicitlyTypedParameterList = unboundLambda.HasExplicitlyTypedParameterList; var numDelegateParameters = parameterTypes.Length; + bool underscoreMeansDiscard = unboundLambda.UnderscoreMeansDiscard; for (int p = 0; p < unboundLambda.ParameterCount; ++p) { // If there are no types given in the lambda then used the delegate type. @@ -361,7 +362,10 @@ private ImmutableArray MakeParameters( var name = unboundLambda.ParameterName(p); var location = unboundLambda.ParameterLocation(p); var locations = location == null ? ImmutableArray.Empty : ImmutableArray.Create(location); - var parameter = new SourceSimpleParameterSymbol(this, type, p, refKind, name, locations); + + var parameter = (underscoreMeansDiscard && name == "_") + ? (ParameterSymbol)new DiscardParameterSymbol(owner: this, type, ordinal: p, refKind, locations) + : new SourceSimpleParameterSymbol(owner: this, type, ordinal: p, refKind, name, locations); builder.Add(parameter); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index 6de65618c32df..e18c1938a701c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -9,9 +9,42 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// A source parameter that has no default value, no attributes, /// and is not params. /// - internal sealed class SourceSimpleParameterSymbol : SourceParameterSymbol + internal sealed class SourceSimpleParameterSymbol : SourceSimpleParameterSymbolBase { public SourceSimpleParameterSymbol( + Symbol owner, + TypeWithAnnotations parameterType, + int ordinal, + RefKind refKind, + string name, + ImmutableArray locations) + : base(owner, parameterType, ordinal, refKind, name, locations) + { + } + } + + internal sealed class DiscardParameterSymbol : SourceSimpleParameterSymbolBase, IDiscardSymbol + { + public DiscardParameterSymbol( + Symbol owner, + TypeWithAnnotations parameterType, + int ordinal, + RefKind refKind, + ImmutableArray locations) + : base(owner, parameterType, ordinal, refKind, name: "", locations) + { + } + + ITypeSymbol IDiscardSymbol.Type + => Type; + + CodeAnalysis.NullableAnnotation IDiscardSymbol.NullableAnnotation + => TypeWithAnnotations.ToPublicAnnotation(); + } + + internal abstract class SourceSimpleParameterSymbolBase : SourceParameterSymbol + { + public SourceSimpleParameterSymbolBase( Symbol owner, TypeWithAnnotations parameterType, int ordinal, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index f798131fceacb..c6c1f8380ccee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -839,6 +839,11 @@ indexování mobilních vyrovnávacích pamětí pevné velikosti + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions skrývání názvů ve vnořených funkcích diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ead41e525ff5f..05cf96985ae8b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -839,6 +839,11 @@ Bewegliche Puffer fester Größe werden indiziert. + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions Namensshadowing in geschachtelten Funktionen diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 27ee61a55ea20..16f12b519f989 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -840,6 +840,11 @@ indexando búferes fijos movibles + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions sombreado de nombres en funciones anidadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 917d821485d6d..4633e814b6ad7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -839,6 +839,11 @@ indexation de mémoires tampons fixes mobiles + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions ombrage des noms dans les fonctions imbriquées diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 473bcf504a41f..a416d3ef870bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -839,6 +839,11 @@ indicizzazione di buffer fissi mobili + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions shadowing dei nomi nelle funzioni annidate diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e8c722fac142b..fb268e7ea6abb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -839,6 +839,11 @@ 移動可能な固定バッファーのインデックス化 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 入れ子になった関数での名前シャドウイング diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3529cbb5e579c..545f933b84321 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -839,6 +839,11 @@ 이동 가능한 고정 버퍼 인덱싱 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 중첩된 함수의 이름 섀도잉 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c2746c8398c98..4f448cf06c787 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -839,6 +839,11 @@ indeksowanie możliwych do przenoszenia buforów fixed + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions zasłanianie nazw w funkcjach zagnieżdżonych diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 3b9ae5e0bb98a..34c22baa89db9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -839,6 +839,11 @@ buffers fixos móveis de indexação + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions sombreamento de nome em funções aninhadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index c48a90f98eeff..5528e5c46fb66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -839,6 +839,11 @@ индексирование перемещаемых буферов фиксированного размера + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions скрытие имен во вложенных функциях diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 184c5c80c8a16..a499f7d76c762 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -839,6 +839,11 @@ taşınabilir sabit arabellekler dizine alınıyor + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions iç içe işlevlerde ad gölgeleme diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 8d2e2a8f34f0e..cd58137933b6c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -839,6 +839,11 @@ 正在编制可移动固定缓冲区的索引 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 在嵌套函数中的名称映射 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 97556e4b102f4..2c04cbcb911fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -839,6 +839,11 @@ 對可移動的固定緩衝區編製索引 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 巢狀函式中的名稱鏡像處理 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs new file mode 100644 index 0000000000000..07739acf898de --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -0,0 +1,219 @@ +// 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.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + [CompilerTrait(CompilerFeature.LambdaDiscardParameters)] + public class LambdaDiscardParametersTests : CompilingTestBase + { + // This method should be removed once the lambda discard parameters feature is slotted into a C# language version + public new static CSharpCompilation CreateCompilation( + CSharpTestSource source, + System.Collections.Generic.IEnumerable references = null, + CSharpCompilationOptions options = null, + CSharpParseOptions parseOptions = null, + Roslyn.Test.Utilities.TargetFramework targetFramework = Roslyn.Test.Utilities.TargetFramework.Standard, + string assemblyName = "", + string sourceFileName = "", + bool skipUsesIsNullable = false) + => CSharpTestBase.CreateCompilation(source, references, options, parseOptions: parseOptions ?? TestOptions.RegularPreview, targetFramework, assemblyName, sourceFileName, skipUsesIsNullable); + + [Fact] + public void DiscardParameters_CSharp8() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (_, _) => 3L; + System.Console.WriteLine(f1(1, null)); + + System.Func f2 = (a, _, + _) => 4L; + + System.Func f3 = (_, a, + _) => 5L; + + System.Func f4 = (_, + _, + _) => 6L; + + System.Func f5 = (_, + _, + a) => 7L; + } +}", parseOptions: TestOptions.Regular8); + + comp.VerifyDiagnostics( + // (6,51): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Func f1 = (_, _) => 3L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(6, 51), + // (10,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _) => 4L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(10, 13), + // (13,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _) => 5L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(13, 13), + // (16,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(16, 13), + // (17,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _) => 6L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(17, 13), + // (20,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // _, + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(20, 13) + ); + + var tree = comp.SyntaxTrees.Single(); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); + VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); + } + + private static void VerifyDiscardParameterSymbol(ParameterSyntax underscore, string expectedType, CodeAnalysis.NullableAnnotation expectedAnnotation, SemanticModel model) + { + Assert.Null(model.GetSymbolInfo(underscore).Symbol); + var symbol1 = model.GetDeclaredSymbol(underscore); + Assert.Equal(expectedType, symbol1.Type.ToTestDisplayString()); + Assert.Equal("", symbol1.Name); + + var discard1 = (IDiscardSymbol)symbol1; + Assert.Equal(expectedType, discard1.Type.ToTestDisplayString()); + Assert.Equal(expectedAnnotation, discard1.NullableAnnotation); + } + + [Fact] + public void DiscardParameters() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (_, _) => 3L; + System.Console.Write(f1(0, 0)); + + System.Func f2 = (_, _, a) => 4L + a; + System.Console.Write(f2(0, 0, 1)); + + System.Func f3 = (a, _, _) => 5L + a; + System.Console.Write(f3(1, 0, 0)); + } +}", options: TestOptions.DebugExe); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "356"); + } + + [Fact] + public void DiscardParameters_WithTypes() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (short _, short _) => 3L; + System.Console.Write(f1(0, 0)); + + System.Func f2 = (short _, short _, int a) => 4L + a; + System.Console.Write(f2(0, 0, 1)); + + System.Func f3 = (int a, short _, short _) => 5L + a; + System.Console.Write(f3(1, 0, 0)); + } +}", options: TestOptions.DebugExe); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "356"); + } + + [Fact] + public void DiscardParameters_InDelegates() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = delegate(int _, int _) { return 3L; }; + System.Console.Write(f1(0, 0)); + + System.Func f2 = delegate(int _, int _, int a) { return 4L + a; }; + System.Console.Write(f2(0, 0, 1)); + } +}", options: TestOptions.DebugExe); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "35"); + } + + [Fact] + public void DiscardParameters_NotInScope() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f = (_, _) => _; + } +}"); + + comp.VerifyDiagnostics( + // (6,52): error CS0103: The name '_' does not exist in the current context + // System.Func f = (_, _) => _; + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(6, 52) + ); + + var tree = comp.SyntaxTrees.Single(); + var underscoreParameters = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + VerifyDiscardParameterSymbol(underscoreParameters[0], "System.Int32", CodeAnalysis.NullableAnnotation.NotAnnotated, model); + VerifyDiscardParameterSymbol(underscoreParameters[1], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); + + var underscore = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").Single(); + Assert.Null(model.GetSymbolInfo(underscore).Symbol); + } + + [Fact] + public void DiscardParameters_NotADiscardWhenSingleUnderscore() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f = (a, _) => _; + System.Console.Write(f(1, 2)); + + System.Func g = (_, a) => _; + System.Console.Write(g(1, 2)); + } +}", options: TestOptions.DebugExe); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "21"); + + var tree = comp.SyntaxTrees.Single(); + var underscoreParameters = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var parameterSymbol1 = model.GetDeclaredSymbol(underscoreParameters[0]); + Assert.NotNull(parameterSymbol1); + Assert.IsNotType(typeof(IDiscardSymbol), parameterSymbol1); + + var parameterSymbol2 = model.GetDeclaredSymbol(underscoreParameters[1]); + Assert.NotNull(parameterSymbol2); + Assert.IsNotType(typeof(IDiscardSymbol), parameterSymbol2); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index ac0c2aca30ff5..19a9a372bdefe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -16,6 +16,58 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class LambdaTests : CompilingTestBase { + [Fact] + public void DiscardParameters() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f = (_, _) => 3; + System.Console.WriteLine(f(1, 2)); + } +}"); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "3"); + } + + [Fact] + public void DiscardParameters_NotInScope() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f = (_, _) => _; + } +}"); + + comp.VerifyDiagnostics(); + } + + [Fact] + public void DiscardParameters_NotADiscardWhenSingle() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f = (a, _) => _; + System.Console.WriteLine(f(1, 2)); + + System.Func g = (_, a) => _; + System.Console.WriteLine(f(1, 2)); + } +}"); + + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "21"); + } + [Fact, WorkItem(37456, "https://github.com/dotnet/roslyn/issues/37456")] public void Verify37456() { diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs index b578a7a354371..43353ae168ae2 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs @@ -1119,6 +1119,24 @@ await TestInMethodAsync( expected: Classifications(Identifier("_"), Operators.Equals, Number("1"), Punctuation.Semicolon)); } + [Fact] + public async Task UnderscoreInLambda() + { + await TestInMethodAsync( + code: @"x = (_) => 1;", + expected: Classifications(Identifier("x"), Operators.Equals, Punctuation.OpenParen, Parameter("_"), Punctuation.CloseParen, + Operators.EqualsGreaterThan, Number("1"), Punctuation.Semicolon)); + } + + [Fact] + public async Task DiscardInLambda() + { + await TestInMethodAsync( + code: @"x = (_, _) => 1;", + expected: Classifications(Identifier("x"), Operators.Equals, Punctuation.OpenParen, Parameter("_"), Punctuation.Comma, Parameter("_"), Punctuation.CloseParen, + Operators.EqualsGreaterThan, Number("1"), Punctuation.Semicolon)); + } + [Fact] public async Task UnderscoreInAssignment() { diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 8bd79edcefab8..a7cb3785a9182 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -2663,6 +2663,34 @@ void M() }"); // No quick info (see issue #16667) } + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestLambdaDiscardParameter_FirstDiscard() + { + await TestAsync( +@"class C +{ + void M() + { + System.Func f = ($$_, _) => 1; + } +}", + MainDescription($"({FeaturesResources.parameter}) string ")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestLambdaDiscardParameter_SecondDiscard() + { + await TestAsync( +@"class C +{ + void M() + { + System.Func f = (_, $$_) => 1; + } +}", + MainDescription($"({FeaturesResources.parameter}) int ")); + } + [WorkItem(540871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540871")] [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestLiterals() diff --git a/src/EditorFeatures/CSharpTest/UseLocalFunction/UseLocalFunctionTests.cs b/src/EditorFeatures/CSharpTest/UseLocalFunction/UseLocalFunctionTests.cs index 49581ac7c3d29..3fbbfe08df417 100644 --- a/src/EditorFeatures/CSharpTest/UseLocalFunction/UseLocalFunctionTests.cs +++ b/src/EditorFeatures/CSharpTest/UseLocalFunction/UseLocalFunctionTests.cs @@ -3685,6 +3685,28 @@ static void Main(string[] args) { static string? f(string? s) => s; } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseLocalFunction)] + public async Task TestWithDiscardParameters() + { + await TestInRegularAndScriptAsync( +@" +class Program +{ + static void Main(string[] args) + { + System.Func [||]f = (_, _, a) => 1; + } +}", +@" +class Program +{ + static void Main(string[] args) + { + static long f(int _, string _, int a) => 1; + } }"); } } diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index f1b3853c36e6d..bb056dd4997aa 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -35,5 +35,6 @@ public enum CompilerFeature AsyncStreams, NullableReferenceTypes, DefaultInterfaceImplementation, + LambdaDiscardParameters, } } From c8ba784f06f5a79f18c59862f390a5cb74d10270 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 23 Sep 2019 10:59:58 -0700 Subject: [PATCH 02/27] Remove duplicate tests --- .../Test/Semantic/Semantics/LambdaTests.cs | 56 +------------------ 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 19a9a372bdefe..db585e6ea26eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -16,58 +16,6 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class LambdaTests : CompilingTestBase { - [Fact] - public void DiscardParameters() - { - var comp = CreateCompilation(@" -public class C -{ - public static void Main() - { - System.Func f = (_, _) => 3; - System.Console.WriteLine(f(1, 2)); - } -}"); - - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "3"); - } - - [Fact] - public void DiscardParameters_NotInScope() - { - var comp = CreateCompilation(@" -public class C -{ - public static void Main() - { - System.Func f = (_, _) => _; - } -}"); - - comp.VerifyDiagnostics(); - } - - [Fact] - public void DiscardParameters_NotADiscardWhenSingle() - { - var comp = CreateCompilation(@" -public class C -{ - public static void Main() - { - System.Func f = (a, _) => _; - System.Console.WriteLine(f(1, 2)); - - System.Func g = (_, a) => _; - System.Console.WriteLine(f(1, 2)); - } -}"); - - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "21"); - } - [Fact, WorkItem(37456, "https://github.com/dotnet/roslyn/issues/37456")] public void Verify37456() { @@ -3391,9 +3339,9 @@ static void M() void verifyDiagnostics() { comp.VerifyDiagnostics( - // (8,37): error CS0100: The parameter name '_' is a duplicate + // (8,37): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Func f = (_, _) => 0; - Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(8, 37)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(8, 37)); } } From e8ed9dd1c4543b26935badf39be910a9c4efcb43 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 23 Sep 2019 11:51:05 -0700 Subject: [PATCH 03/27] Factor logic to recognize underscore tokens --- .../Binder/Binder.QueryUnboundLambdaState.cs | 2 +- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/Binder/Binder_Lambda.cs | 38 ++++++++++++++++--- .../Portable/Binder/Binder_Operators.cs | 2 +- .../Portable/Binder/SwitchBinder_Patterns.cs | 2 +- .../Portable/BoundTree/UnboundLambda.cs | 38 ++++++------------- .../CSharp/Portable/CSharpExtensions.cs | 5 +++ .../Portable/Symbols/Source/LambdaSymbol.cs | 3 +- .../Semantics/LambdaDiscardParametersTests.cs | 22 +++++++++++ 9 files changed, 77 insertions(+), 39 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index b516d18544ca1..cf0e1753f3095 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -26,8 +26,8 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, _bodyFactory = bodyFactory; } - public override bool UnderscoreMeansDiscard { get { return false; } } public override string ParameterName(int index) { return _parameters[index].Name; } + public override bool ParameterIsDiscard(int index) { return false; } public override bool HasSignature { get { return true; } } public override bool HasExplicitlyTypedParameterList { get { return false; } } public override int ParameterCount { get { return _parameters.Length; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e690b83d2abce..23d56ad276b74 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1422,7 +1422,7 @@ private BoundExpression BindIdentifier( /// private static bool FallBackOnDiscard(IdentifierNameSyntax node, DiagnosticBag diagnostics) { - if (node.Identifier.ContextualKind() != SyntaxKind.UnderscoreToken) + if (!node.Identifier.IsUnderscoreToken()) { return false; } @@ -1439,7 +1439,7 @@ private static bool FallBackOnDiscard(IdentifierNameSyntax node, DiagnosticBag d private static bool IsOutVarDiscardIdentifier(SimpleNameSyntax node) { - Debug.Assert(node.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken); + Debug.Assert(node.Identifier.IsUnderscoreToken()); CSharpSyntaxNode parent = node.Parent; return (parent?.Kind() == SyntaxKind.Argument && diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 58d1f9d29e566..2e4cca56a38e6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -31,7 +31,7 @@ internal partial class Binder // If we have no modifiers then the modifiers array is null; if we have any modifiers // then the modifiers array is non-null and not empty. - private (ImmutableArray, ImmutableArray, ImmutableArray, bool) AnalyzeAnonymousFunction( + private (ImmutableArray, ImmutableArray, ImmutableArray, ImmutableArray, bool) AnalyzeAnonymousFunction( CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { Debug.Assert(syntax != null); @@ -43,6 +43,7 @@ internal partial class Binder bool isAsync = false; var namesBuilder = ArrayBuilder.GetInstance(); + ImmutableArray discardsOpt = default; SeparatedSyntaxList? parameterSyntaxList = null; bool hasSignature; @@ -94,8 +95,14 @@ internal partial class Binder // However, we still want to give errors on every bad type in the list, even if one // is missing. + int underscoresCount = 0; foreach (var p in parameterSyntaxList.Value) { + if (p.Identifier.IsUnderscoreToken()) + { + underscoresCount++; + } + foreach (var attributeList in p.AttributeLists) { Error(diagnostics, ErrorCode.ERR_AttributesNotAllowed, attributeList); @@ -161,6 +168,8 @@ internal partial class Binder refKindsBuilder.Add(refKind); } + discardsOpt = computeDiscards(parameterSyntaxList.Value, underscoresCount); + if (hasExplicitlyTypedParameterList) { types = typesBuilder.ToImmutable(); @@ -182,7 +191,24 @@ internal partial class Binder namesBuilder.Free(); - return (refKinds, types, names, isAsync); + return (refKinds, types, names, discardsOpt, isAsync); + + static ImmutableArray computeDiscards(SeparatedSyntaxList parameters, int underscoresCount) + { + if (underscoresCount <= 1) + { + return default; + } + + // When there are two or more underscores, they are discards + var discardsBuilder = ArrayBuilder.GetInstance(parameters.Count); + foreach (var p in parameters) + { + discardsBuilder.Add(p.Identifier.IsUnderscoreToken()); + } + + return discardsBuilder.ToImmutableAndFree(); + } } private void CheckParenthesizedLambdaParameters( @@ -216,7 +242,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB Debug.Assert(syntax != null); Debug.Assert(syntax.IsAnonymousFunction()); - var (refKinds, types, names, isAsync) = AnalyzeAnonymousFunction(syntax, diagnostics); + var (refKinds, types, names, discardsOpt, isAsync) = AnalyzeAnonymousFunction(syntax, diagnostics); if (!types.IsDefault) { foreach (var type in types) @@ -229,13 +255,12 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB } } - var lambda = new UnboundLambda(syntax, this, refKinds, types, names, isAsync); + var lambda = new UnboundLambda(syntax, this, refKinds, types, names, discardsOpt, isAsync); if (!names.IsDefault) { var binder = new LocalScopeBinder(this); bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions); var pNames = PooledHashSet.GetInstance(); - bool underscoreMeansDiscard = lambda.UnderscoreMeansDiscard; bool seenDiscard = false; for (int i = 0; i < lambda.ParameterCount; i++) @@ -247,10 +272,11 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB continue; } - if (name == "_" && underscoreMeansDiscard) + if (lambda.ParameterIsDiscard(i)) { if (seenDiscard) { + // We only report the diagnostic on the second and subsequent underscores MessageID.IDS_FeatureLambdaDiscardParameters.CheckFeatureAvailability( diagnostics, binder.Compilation, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index e4ed046b8e4b4..ec83a9c553e91 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2752,7 +2752,7 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa TypeWithAnnotations targetTypeWithAnnotations = BindType(node.Right, isTypeDiagnostics, out alias); TypeSymbol targetType = targetTypeWithAnnotations.Type; - bool wasUnderscore = node.Right is IdentifierNameSyntax name && name.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken; + bool wasUnderscore = node.Right is IdentifierNameSyntax name && name.Identifier.IsUnderscoreToken(); if (!wasUnderscore && targetType?.IsErrorType() == true && isTypeDiagnostics.HasAnyResolvedErrors() && ((CSharpParseOptions)node.SyntaxTree.Options).IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching)) { diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index b0b70d00fbf04..2a9ee508e7c16 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -288,7 +288,7 @@ private BoundSwitchLabel BindSwitchSectionLabel( void reportIfConstantNamedUnderscore(BoundPattern pattern, ExpressionSyntax expression) { if (!pattern.HasErrors && - expression is IdentifierNameSyntax name && name.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken) + expression is IdentifierNameSyntax name && name.Identifier.IsUnderscoreToken()) { diagnostics.Add(ErrorCode.WRN_CaseConstantNamedUnderscore, expression.Location); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 35d396ab49157..3d92cb0507052 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -327,13 +327,14 @@ public UnboundLambda( ImmutableArray refKinds, ImmutableArray types, ImmutableArray names, + ImmutableArray discardsOpt, bool isAsync, bool hasErrors = false) : base(BoundKind.UnboundLambda, syntax, null, hasErrors || !types.IsDefault && types.Any(t => t.Type?.Kind == SymbolKind.ErrorType)) { Debug.Assert(binder != null); Debug.Assert(syntax.IsAnonymousFunction()); - this.Data = new PlainUnboundLambdaState(this, binder, names, types, refKinds, isAsync); + this.Data = new PlainUnboundLambdaState(this, binder, names, discardsOpt, types, refKinds, isAsync); } private UnboundLambda(UnboundLambda other, Binder binder, NullableWalker.VariableState nullableState) : @@ -376,7 +377,7 @@ public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTyp public TypeSymbol ParameterType(int index) { return ParameterTypeWithAnnotations(index).Type; } public Location ParameterLocation(int index) { return Data.ParameterLocation(index); } public string ParameterName(int index) { return Data.ParameterName(index); } - public bool UnderscoreMeansDiscard { get { return Data.UnderscoreMeansDiscard; } } + public bool ParameterIsDiscard(int index) { return Data.ParameterIsDiscard(index); } } internal abstract class UnboundLambdaState @@ -416,7 +417,7 @@ public void SetUnboundLambda(UnboundLambda unbound) public abstract MessageID MessageID { get; } public abstract string ParameterName(int index); - public abstract bool UnderscoreMeansDiscard { get; } + public abstract bool ParameterIsDiscard(int index); public abstract bool HasSignature { get; } public abstract bool HasExplicitlyTypedParameterList { get; } public abstract int ParameterCount { get; } @@ -1080,6 +1081,7 @@ private static int CanonicallyCompareDiagnostics(Diagnostic x, Diagnostic y) internal class PlainUnboundLambdaState : UnboundLambdaState { private readonly ImmutableArray _parameterNames; + private readonly ImmutableArray _parameterIsDiscardOpt; private readonly ImmutableArray _parameterTypesWithAnnotations; private readonly ImmutableArray _parameterRefKinds; private readonly bool _isAsync; @@ -1088,40 +1090,19 @@ internal PlainUnboundLambdaState( UnboundLambda unboundLambda, Binder binder, ImmutableArray parameterNames, + ImmutableArray parameterIsDiscardOpt, ImmutableArray parameterTypesWithAnnotations, ImmutableArray parameterRefKinds, bool isAsync) : base(binder, unboundLambda) { _parameterNames = parameterNames; + _parameterIsDiscardOpt = parameterIsDiscardOpt; _parameterTypesWithAnnotations = parameterTypesWithAnnotations; _parameterRefKinds = parameterRefKinds; _isAsync = isAsync; } - public override bool UnderscoreMeansDiscard - { - get - { - bool foundOneUnderscore = false; - foreach (var name in _parameterNames) - { - if (name == "_") - { - if (foundOneUnderscore) - { - // found multiple underscores - return true; - } - - foundOneUnderscore = true; - } - } - - return false; - } - } - public override bool HasSignature { get { return !_parameterNames.IsDefault; } } public override bool HasExplicitlyTypedParameterList { get { return !_parameterTypesWithAnnotations.IsDefault; } } @@ -1164,6 +1145,11 @@ public override string ParameterName(int index) return _parameterNames[index]; } + public override bool ParameterIsDiscard(int index) + { + return _parameterIsDiscardOpt.IsDefault ? false : _parameterIsDiscardOpt[index]; + } + public override RefKind RefKind(int index) { Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length); diff --git a/src/Compilers/CSharp/Portable/CSharpExtensions.cs b/src/Compilers/CSharp/Portable/CSharpExtensions.cs index e3062343b1029..6623f99e6057e 100644 --- a/src/Compilers/CSharp/Portable/CSharpExtensions.cs +++ b/src/Compilers/CSharp/Portable/CSharpExtensions.cs @@ -39,6 +39,11 @@ internal static SyntaxKind ContextualKind(this SyntaxToken token) return (object)token.Language == (object)LanguageNames.CSharp ? (SyntaxKind)token.RawContextualKind : SyntaxKind.None; } + internal static bool IsUnderscoreToken(this SyntaxToken identifier) + { + return identifier.ContextualKind() == SyntaxKind.UnderscoreToken; + } + /// /// Returns the index of the first node of a specified kind in the node list. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 7fd029fb249f2..c4ec7b833b6c3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -332,7 +332,6 @@ private ImmutableArray MakeParameters( var hasExplicitlyTypedParameterList = unboundLambda.HasExplicitlyTypedParameterList; var numDelegateParameters = parameterTypes.Length; - bool underscoreMeansDiscard = unboundLambda.UnderscoreMeansDiscard; for (int p = 0; p < unboundLambda.ParameterCount; ++p) { // If there are no types given in the lambda then used the delegate type. @@ -363,7 +362,7 @@ private ImmutableArray MakeParameters( var location = unboundLambda.ParameterLocation(p); var locations = location == null ? ImmutableArray.Empty : ImmutableArray.Create(location); - var parameter = (underscoreMeansDiscard && name == "_") + var parameter = unboundLambda.ParameterIsDiscard(p) ? (ParameterSymbol)new DiscardParameterSymbol(owner: this, type, ordinal: p, refKind, locations) : new SourceSimpleParameterSymbol(owner: this, type, ordinal: p, refKind, name, locations); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 07739acf898de..758e55786920c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -113,6 +113,28 @@ public static void Main() CompileAndVerify(comp, expectedOutput: "356"); } + [Fact] + public void DiscardParameters_UnicodeUnderscore() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (\u005f, \u005f) => 3L; + \u005f = 1; + } +}"); + comp.VerifyDiagnostics( + // (6,55): error CS0100: The parameter name '_' is a duplicate + // System.Func f1 = (\u005f, \u005f) => 3L; + Diagnostic(ErrorCode.ERR_DuplicateParamName, @"\u005f").WithArguments("_").WithLocation(6, 55), + // (7,9): error CS0103: The name '_' does not exist in the current context + // \u005f = 1; + Diagnostic(ErrorCode.ERR_NameNotInContext, @"\u005f").WithArguments("_").WithLocation(7, 9) + ); + } + [Fact] public void DiscardParameters_WithTypes() { From e9389c03aaddf4393da604fb70502990aa7ce6c0 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 23 Sep 2019 13:02:47 -0700 Subject: [PATCH 04/27] Align symbol display with other discards --- .../Portable/Symbols/Source/SourceSimpleParameterSymbol.cs | 2 +- .../Test/Semantic/Semantics/LambdaDiscardParametersTests.cs | 2 +- .../CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index e18c1938a701c..e36b79de8fac8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -31,7 +31,7 @@ public DiscardParameterSymbol( int ordinal, RefKind refKind, ImmutableArray locations) - : base(owner, parameterType, ordinal, refKind, name: "", locations) + : base(owner, parameterType, ordinal, refKind, name: "_", locations) { } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 758e55786920c..a2788982b8dce 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -83,7 +83,7 @@ private static void VerifyDiscardParameterSymbol(ParameterSyntax underscore, str Assert.Null(model.GetSymbolInfo(underscore).Symbol); var symbol1 = model.GetDeclaredSymbol(underscore); Assert.Equal(expectedType, symbol1.Type.ToTestDisplayString()); - Assert.Equal("", symbol1.Name); + Assert.Equal("_", symbol1.Name); var discard1 = (IDiscardSymbol)symbol1; Assert.Equal(expectedType, discard1.Type.ToTestDisplayString()); diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index a7cb3785a9182..84dfbc7f0bd34 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -2674,7 +2674,7 @@ void M() System.Func f = ($$_, _) => 1; } }", - MainDescription($"({FeaturesResources.parameter}) string ")); + MainDescription($"({FeaturesResources.parameter}) string _")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -2688,7 +2688,7 @@ void M() System.Func f = (_, $$_) => 1; } }", - MainDescription($"({FeaturesResources.parameter}) int ")); + MainDescription($"({FeaturesResources.parameter}) int _")); } [WorkItem(540871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540871")] From 43e255f99c4af2feeafc2e8b6d395a0b79a26bca Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 23 Sep 2019 13:27:49 -0700 Subject: [PATCH 05/27] Distinguish discards in QuickInfo --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 8 ++++---- .../Core/Portable/FeaturesResources.Designer.cs | 9 +++++++++ src/Features/Core/Portable/FeaturesResources.resx | 3 +++ ...splayService.AbstractSymbolDescriptionBuilder.cs | 13 ++++++++++++- .../Core/Portable/xlf/FeaturesResources.cs.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.de.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.es.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.fr.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.it.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ja.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ko.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.pl.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.pt-BR.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ru.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.tr.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.zh-Hans.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.zh-Hant.xlf | 5 +++++ 17 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 84dfbc7f0bd34..27cdc9b36c7ba 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -2582,7 +2582,7 @@ int M() $$_ = M(); } }", - MainDescription("int _")); + MainDescription($"({FeaturesResources.discard}) int _")); } [WorkItem(16662, "https://github.com/dotnet/roslyn/issues/16662")] @@ -2613,7 +2613,7 @@ void M(out int i) i = 0; } }", - MainDescription($"int _")); + MainDescription($"({FeaturesResources.discard}) int _")); } [WorkItem(16667, "https://github.com/dotnet/roslyn/issues/16667")] @@ -2674,7 +2674,7 @@ void M() System.Func f = ($$_, _) => 1; } }", - MainDescription($"({FeaturesResources.parameter}) string _")); + MainDescription($"({FeaturesResources.discard}) string _")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -2688,7 +2688,7 @@ void M() System.Func f = (_, $$_) => 1; } }", - MainDescription($"({FeaturesResources.parameter}) int _")); + MainDescription($"({FeaturesResources.discard}) int _")); } [WorkItem(540871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540871")] diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 0c75a6e825fbf..c56f30167ea78 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -1274,6 +1274,15 @@ internal static string Deleting_captured_variable_0_will_prevent_the_debug_sessi } } + /// + /// Looks up a localized string similar to discard. + /// + internal static string discard { + get { + return ResourceManager.GetString("discard", resourceCulture); + } + } + /// /// Looks up a localized string similar to Disposable field '{0}' is never disposed. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index fc5ef5187e04b..78ecd50e862db 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -315,6 +315,9 @@ parameter + + discard + in diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index 4de2c40f3bb91..b3e4ed35231fc 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -264,7 +264,11 @@ private async Task AddDescriptionPartAsync(ISymbol symbol) AddDeprecatedPrefix(); } - if (symbol is IDynamicTypeSymbol) + if (symbol is IDiscardSymbol discard) + { + AddDescriptionForDiscard(discard); + } + else if (symbol is IDynamicTypeSymbol) { AddDescriptionForDynamicType(); } @@ -599,6 +603,13 @@ private async Task AddDescriptionForParameterAsync(IParameterSymbol symbol) ToMinimalDisplayParts(symbol, MinimallyQualifiedFormatWithConstants)); } + private void AddDescriptionForDiscard(IDiscardSymbol symbol) + { + AddToGroup(SymbolDescriptionGroups.MainDescription, + Description(FeaturesResources.discard), + ToMinimalDisplayParts(symbol, MinimallyQualifiedFormatWithConstants)); + } + protected void AddDescriptionForProperty(IPropertySymbol symbol) { AddToGroup(SymbolDescriptionGroups.MainDescription, diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 7671948c48e86..ff466e13b346e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -692,6 +692,11 @@ {0} se dá zjednodušit. + + discard + discard + + generic overload obecné přetížení diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 00186664c671f..2f7f1e7aca5af 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -692,6 +692,11 @@ {0} kann vereinfacht werden + + discard + discard + + generic overload generische Überladung diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 53d4e97cf3bdb..153a825af1af7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -692,6 +692,11 @@ {0} se puede simplificar. + + discard + discard + + generic overload sobrecarga genérica diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 5d531ff13eb72..43a1783a9cc34 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -692,6 +692,11 @@ {0} peut être simplifié + + discard + discard + + generic overload surcharge générique diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 535b109b7a033..3fe0c3f696ced 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -692,6 +692,11 @@ {0} può essere semplificato + + discard + discard + + generic overload overload generico diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 99792113111dd..a9175a02cccbf 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -692,6 +692,11 @@ {0} を簡略化できます。 + + discard + discard + + generic overload ジェネリック オーバーロード diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 5b4b9df154a99..490812769be99 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -692,6 +692,11 @@ {0}은(는) 단순화될 수 있습니다. + + discard + discard + + generic overload 제네릭 오버로드 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 7614df533daa7..11b5904c90078 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -692,6 +692,11 @@ Element {0} można uprościć + + discard + discard + + generic overload przeciążenie ogólne diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 07418ed1685e6..d1144e38c42e5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -692,6 +692,11 @@ {0} pode ser simplificado + + discard + discard + + generic overload sobrecarga genérica diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 7545a721e880a..0ca4d2972f8b6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -692,6 +692,11 @@ {0} можно упростить + + discard + discard + + generic overload универсальная перегрузка diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index e63c7714e1c95..8f68c37e723d3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -692,6 +692,11 @@ {0} basitleştirilebilir + + discard + discard + + generic overload genel aşırı yükleme diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index f5680627af6d4..a9dd3a574b6a8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -692,6 +692,11 @@ {0} 可以简化 + + discard + discard + + generic overload 泛型重载 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 5e54f1371614d..0e147134020cd 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -692,6 +692,11 @@ 可簡化 {0} + + discard + discard + + generic overload 泛型多載 From 6758a7674b56eca55136a71f5041666fa42ee506 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 24 Sep 2019 14:11:47 -0700 Subject: [PATCH 06/27] Add IsDiscard property instead of IDiscardSymbol --- .../Binder/WithLambdaParametersBinder.cs | 4 +- .../Symbols/Metadata/PE/PEParameterSymbol.cs | 10 +--- .../Portable/Symbols/ParameterSymbol.cs | 5 ++ .../Symbols/SignatureOnlyParameterSymbol.cs | 2 + .../Portable/Symbols/Source/LambdaSymbol.cs | 4 +- .../Source/SourceClonedParameterSymbol.cs | 2 + .../Source/SourceComplexParameterSymbol.cs | 2 + .../Symbols/Source/SourceParameterSymbol.cs | 2 +- .../Source/SourceSimpleParameterSymbol.cs | 39 ++----------- .../Symbols/Source/ThisParameterSymbol.cs | 12 ++-- .../Synthesized/SynthesizedParameterSymbol.cs | 12 ++-- .../Symbols/Wrapped/WrappedParameterSymbol.cs | 10 +--- .../Semantics/LambdaDiscardParametersTests.cs | 58 ++++++++++++++++--- .../Core/Portable/PublicAPI.Unshipped.txt | 3 +- .../Core/Portable/Symbols/IParameterSymbol.cs | 5 ++ .../Portable/Symbols/ParameterSymbol.vb | 6 ++ 16 files changed, 99 insertions(+), 77 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs index cef52511fd004..10c4a8f343cc1 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs @@ -28,7 +28,7 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) RecordDefinitions(parameters); foreach (var parameter in lambdaSymbol.Parameters) { - if (!(parameter is IDiscardSymbol)) + if (!parameter.IsDiscard) { this.parameterMap.Add(parameter.Name, parameter); } @@ -41,7 +41,7 @@ private void RecordDefinitions(ImmutableArray definitions) var declarationMap = _definitionMap ?? (_definitionMap = new SmallDictionary()); foreach (var s in definitions) { - if (!(s is IDiscardSymbol) && !declarationMap.ContainsKey(s.Name)) + if (!s.IsDiscard && !declarationMap.ContainsKey(s.Name)) { declarationMap.Add(s.Name, s); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 1123fc815a3e0..dee10b68bc992 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -287,13 +287,9 @@ private PEParameterSymbol( Debug.Assert(hasNameInMetadata == this.HasNameInMetadata); } - private bool HasNameInMetadata - { - get - { - return _packedFlags.HasNameInMetadata; - } - } + private bool HasNameInMetadata => _packedFlags.HasNameInMetadata; + + public sealed override bool IsDiscard => false; private static PEParameterSymbol Create( PEModuleSymbol moduleSymbol, diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index 2c89021e7505c..fbf40a51f5a5a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -59,6 +59,11 @@ protected override sealed Symbol OriginalSymbolDefinition /// public abstract RefKind RefKind { get; } + /// + /// Returns true if the parameter is a discard parameter. + /// + public abstract bool IsDiscard { get; } + /// /// Custom modifiers associated with the ref modifier, or an empty array if there are none. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs index e3b534ae494b5..3a9f83c2a19c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs @@ -44,6 +44,8 @@ public SignatureOnlyParameterSymbol( public override bool IsImplicitlyDeclared { get { return true; } } + public override bool IsDiscard { get { return false; } } + #region Not used by MethodSignatureComparer internal override bool IsMetadataIn { get { throw ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index c4ec7b833b6c3..92a08d8fc8b14 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -362,9 +362,7 @@ private ImmutableArray MakeParameters( var location = unboundLambda.ParameterLocation(p); var locations = location == null ? ImmutableArray.Empty : ImmutableArray.Create(location); - var parameter = unboundLambda.ParameterIsDiscard(p) - ? (ParameterSymbol)new DiscardParameterSymbol(owner: this, type, ordinal: p, refKind, locations) - : new SourceSimpleParameterSymbol(owner: this, type, ordinal: p, refKind, name, locations); + var parameter = new SourceSimpleParameterSymbol(owner: this, type, ordinal: p, refKind, name, unboundLambda.ParameterIsDiscard(p), locations); builder.Add(parameter); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs index 8525423178455..ab9db68a26ef5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs @@ -29,6 +29,8 @@ internal SourceClonedParameterSymbol(SourceParameterSymbol originalParam, Symbol public override bool IsImplicitlyDeclared => true; + public override bool IsDiscard => _originalParam.IsDiscard; + public override ImmutableArray DeclaringSyntaxReferences { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index f526f84a568df..ccdfa26fbec68 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -77,6 +77,8 @@ internal SourceComplexParameterSymbol( internal SyntaxTree SyntaxTree => _syntaxRef == null ? null : _syntaxRef.SyntaxTree; + public sealed override bool IsDiscard => false; + internal override ConstantValue ExplicitDefaultConstantValue { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index fb7a40da28b3d..2ebe74b3a9a63 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -71,7 +71,7 @@ public static SourceParameterSymbol Create( (syntax.AttributeLists.Count == 0) && !owner.IsPartialMethod()) { - return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, locations); + return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, isDiscard: false, locations); } return new SourceComplexParameterSymbol( diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index e36b79de8fac8..f0630f383f910 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -9,52 +9,23 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// A source parameter that has no default value, no attributes, /// and is not params. /// - internal sealed class SourceSimpleParameterSymbol : SourceSimpleParameterSymbolBase + internal sealed class SourceSimpleParameterSymbol : SourceParameterSymbol { public SourceSimpleParameterSymbol( - Symbol owner, - TypeWithAnnotations parameterType, - int ordinal, - RefKind refKind, - string name, - ImmutableArray locations) - : base(owner, parameterType, ordinal, refKind, name, locations) - { - } - } - - internal sealed class DiscardParameterSymbol : SourceSimpleParameterSymbolBase, IDiscardSymbol - { - public DiscardParameterSymbol( - Symbol owner, - TypeWithAnnotations parameterType, - int ordinal, - RefKind refKind, - ImmutableArray locations) - : base(owner, parameterType, ordinal, refKind, name: "_", locations) - { - } - - ITypeSymbol IDiscardSymbol.Type - => Type; - - CodeAnalysis.NullableAnnotation IDiscardSymbol.NullableAnnotation - => TypeWithAnnotations.ToPublicAnnotation(); - } - - internal abstract class SourceSimpleParameterSymbolBase : SourceParameterSymbol - { - public SourceSimpleParameterSymbolBase( Symbol owner, TypeWithAnnotations parameterType, int ordinal, RefKind refKind, string name, + bool isDiscard, ImmutableArray locations) : base(owner, parameterType, ordinal, refKind, name, locations) { + IsDiscard = isDiscard; } + public override bool IsDiscard { get; } + internal override ConstantValue ExplicitDefaultConstantValue { get { return null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs index 95da88618d6b5..d897a6f42dc8b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs @@ -20,21 +20,19 @@ internal sealed class ThisParameterSymbol : ParameterSymbol internal ThisParameterSymbol(MethodSymbol forMethod) : this(forMethod, forMethod.ContainingType) { } + internal ThisParameterSymbol(MethodSymbol forMethod, TypeSymbol containingType) { _containingMethod = forMethod; _containingType = containingType; } - public override string Name - { - get { return SymbolName; } - } + public override string Name => SymbolName; + + public override bool IsDiscard => false; public override TypeWithAnnotations TypeWithAnnotations - { - get { return TypeWithAnnotations.Create(_containingType, NullableAnnotation.NotAnnotated); } - } + => TypeWithAnnotations.Create(_containingType, NullableAnnotation.NotAnnotated); public override RefKind RefKind { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index cdbf0f7f1be65..3505bb7c810f3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -37,15 +37,11 @@ public SynthesizedParameterSymbolBase( _name = name; } - public override TypeWithAnnotations TypeWithAnnotations - { - get { return _type; } - } + public override TypeWithAnnotations TypeWithAnnotations => _type; - public override RefKind RefKind - { - get { return _refKind; } - } + public override RefKind RefKind => _refKind; + + public sealed override bool IsDiscard => false; internal override bool IsMetadataIn => RefKind == RefKind.In; diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs index 1ae3cafb239c6..14fd139e5d7ba 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs @@ -28,13 +28,9 @@ protected WrappedParameterSymbol(ParameterSymbol underlyingParameter) this._underlyingParameter = underlyingParameter; } - public ParameterSymbol UnderlyingParameter - { - get - { - return _underlyingParameter; - } - } + public ParameterSymbol UnderlyingParameter => _underlyingParameter; + + public sealed override bool IsDiscard => false; #region Forwarded diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index a2788982b8dce..a9cd552173d30 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -84,10 +84,9 @@ private static void VerifyDiscardParameterSymbol(ParameterSyntax underscore, str var symbol1 = model.GetDeclaredSymbol(underscore); Assert.Equal(expectedType, symbol1.Type.ToTestDisplayString()); Assert.Equal("_", symbol1.Name); - - var discard1 = (IDiscardSymbol)symbol1; - Assert.Equal(expectedType, discard1.Type.ToTestDisplayString()); - Assert.Equal(expectedAnnotation, discard1.NullableAnnotation); + Assert.True(symbol1.IsDiscard); + Assert.Equal(expectedType, symbol1.Type.ToTestDisplayString()); + Assert.Equal(expectedAnnotation, symbol1.NullableAnnotation); } [Fact] @@ -98,7 +97,7 @@ public class C { public static void Main() { - System.Func f1 = (_, _) => 3L; + System.Func f1 = (_, _) => { long _ = 3; return _; }; System.Console.Write(f1(0, 0)); System.Func f2 = (_, _, a) => 4L + a; @@ -113,6 +112,29 @@ public static void Main() CompileAndVerify(comp, expectedOutput: "356"); } + [Fact] + public void DiscardParameters_OnLocalFunction() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + local(); + void local(int _, int _) {} + } +}"); + + comp.VerifyDiagnostics( + // (6,9): error CS7036: There is no argument given that corresponds to the required formal parameter '_' of 'local(int, int)' + // local(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "local").WithArguments("_", "local(int, int)").WithLocation(6, 9), + // (7,31): error CS0100: The parameter name '_' is a duplicate + // void local(int _, int _) {} + Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(7, 31) + ); + } + [Fact] public void DiscardParameters_UnicodeUnderscore() { @@ -178,6 +200,28 @@ public static void Main() CompileAndVerify(comp, expectedOutput: "35"); } + [Fact] + public void DiscardParameters_InDelegates_WithAttribute() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = delegate([System.Obsolete]int _, int _ = 0) { return 3L; }; + } +}"); + + comp.VerifyDiagnostics( + // (6,51): error CS7014: Attributes are not valid in this context. + // System.Func f1 = delegate([System.Obsolete]int _, int _ = 0) { return 3L; }; + Diagnostic(ErrorCode.ERR_AttributesNotAllowed, "[System.Obsolete]").WithLocation(6, 51), + // (6,81): error CS1065: Default values are not valid in this context. + // System.Func f1 = delegate([System.Obsolete]int _, int _ = 0) { return 3L; }; + Diagnostic(ErrorCode.ERR_DefaultValueNotAllowed, "=").WithLocation(6, 81) + ); + } + [Fact] public void DiscardParameters_NotInScope() { @@ -231,11 +275,11 @@ public static void Main() var parameterSymbol1 = model.GetDeclaredSymbol(underscoreParameters[0]); Assert.NotNull(parameterSymbol1); - Assert.IsNotType(typeof(IDiscardSymbol), parameterSymbol1); + Assert.False(parameterSymbol1.IsDiscard); var parameterSymbol2 = model.GetDeclaredSymbol(underscoreParameters[1]); Assert.NotNull(parameterSymbol2); - Assert.IsNotType(typeof(IDiscardSymbol), parameterSymbol2); + Assert.False(parameterSymbol2.IsDiscard); } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index bce71d9189c5f..fe7338c4a41b8 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.AsynchronousUsing = 2 Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.Default = 0 -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.Using = 1 -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind Microsoft.CodeAnalysis.Operations.IVariableDeclarationGroupOperation.DeclarationKind.get -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind +Microsoft.CodeAnalysis.IParameterSymbol.IsDiscard.get -> bool Microsoft.CodeAnalysis.SarifVersion Microsoft.CodeAnalysis.SarifVersion.Default = 1 -> Microsoft.CodeAnalysis.SarifVersion Microsoft.CodeAnalysis.SarifVersion.Latest = 2147483647 -> Microsoft.CodeAnalysis.SarifVersion @@ -24,4 +25,4 @@ abstract Microsoft.CodeAnalysis.DataFlowAnalysis.DefinitelyAssignedOnExit.get -> virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.Operations.IPropertySubpatternOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.Operations.IPropertySubpatternOperation operation, TArgument argument) -> TResult -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation, TArgument argument) -> TResult \ No newline at end of file +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation, TArgument argument) -> TResult diff --git a/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs b/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs index d12ba5b803288..0a36770a05a01 100644 --- a/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IParameterSymbol.cs @@ -37,6 +37,11 @@ public interface IParameterSymbol : ISymbol /// bool IsThis { get; } + /// + /// Returns true if the parameter is a discard parameter. + /// + bool IsDiscard { get; } + /// /// Gets the type of the parameter. /// diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb index c5c0a17d743a9..60451133fccc8 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb @@ -65,6 +65,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend ReadOnly Property IsDiscard As Boolean Implements IParameterSymbol.IsDiscard + Get + Return False + End Get + End Property + ''' ''' Describes how the parameter is marshalled when passed to native code. ''' Null if no specific marshalling information is available for the parameter. From 77dda4714663c09cfe616900e6c9ea488a0096e5 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 24 Sep 2019 14:16:55 -0700 Subject: [PATCH 07/27] Adjust IDE logic to use IsDiscard --- ...ractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs | 2 +- .../CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index b3e4ed35231fc..a7df54e563b1b 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -599,7 +599,7 @@ private async Task AddDescriptionForParameterAsync(IParameterSymbol symbol) } AddToGroup(SymbolDescriptionGroups.MainDescription, - Description(FeaturesResources.parameter), + Description(symbol.IsDiscard ? FeaturesResources.discard : FeaturesResources.parameter), ToMinimalDisplayParts(symbol, MinimallyQualifiedFormatWithConstants)); } diff --git a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs index f8fd18d862d44..9c4baaa313137 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationParameterSymbol.cs @@ -62,5 +62,7 @@ public override TResult Accept(SymbolVisitor visitor) public ImmutableArray RefCustomModifiers => ImmutableArray.Create(); public ImmutableArray CustomModifiers => ImmutableArray.Create(); + + public bool IsDiscard => false; } } From 570b3b09fcc6dd81c7ad4260fd955faaf3df756d Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 24 Sep 2019 15:47:06 -0700 Subject: [PATCH 08/27] Avoid large tuple return --- .../CSharp/Portable/Binder/Binder_Lambda.cs | 15 ++++++++------- .../Portable/BoundTree/UnboundLambda.cs | 9 ++++++--- .../RemoveUnusedParametersTests.cs | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 2e4cca56a38e6..8b2c079762db6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -31,7 +31,7 @@ internal partial class Binder // If we have no modifiers then the modifiers array is null; if we have any modifiers // then the modifiers array is non-null and not empty. - private (ImmutableArray, ImmutableArray, ImmutableArray, ImmutableArray, bool) AnalyzeAnonymousFunction( + private UnboundLambda AnalyzeAnonymousFunction( CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { Debug.Assert(syntax != null); @@ -191,7 +191,7 @@ internal partial class Binder namesBuilder.Free(); - return (refKinds, types, names, discardsOpt, isAsync); + return new UnboundLambda(syntax, this, refKinds, types, names, discardsOpt, isAsync); static ImmutableArray computeDiscards(SeparatedSyntaxList parameters, int underscoresCount) { @@ -242,12 +242,14 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB Debug.Assert(syntax != null); Debug.Assert(syntax.IsAnonymousFunction()); - var (refKinds, types, names, discardsOpt, isAsync) = AnalyzeAnonymousFunction(syntax, diagnostics); - if (!types.IsDefault) + var lambda = AnalyzeAnonymousFunction(syntax, diagnostics); + var data = (PlainUnboundLambdaState)lambda.Data; + if (data.HasTypes) { - foreach (var type in types) + for (int i = 0; i < lambda.ParameterCount; i++) { // UNDONE: Where do we report improper use of pointer types? + var type = lambda.Data.ParameterTypeWithAnnotations(i); if (type.HasType && type.IsStatic) { Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, syntax, type.Type); @@ -255,8 +257,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB } } - var lambda = new UnboundLambda(syntax, this, refKinds, types, names, discardsOpt, isAsync); - if (!names.IsDefault) + if (data.HasNames) { var binder = new LocalScopeBinder(this); bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions); diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 3d92cb0507052..028c4064078b1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -424,7 +424,6 @@ public void SetUnboundLambda(UnboundLambda unbound) public abstract bool IsAsync { get; } public abstract Location ParameterLocation(int index); public abstract TypeWithAnnotations ParameterTypeWithAnnotations(int index); - //public abstract SyntaxToken ParameterIdentifier(int index); public abstract RefKind RefKind(int index); protected abstract BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics); @@ -1103,6 +1102,10 @@ internal PlainUnboundLambdaState( _isAsync = isAsync; } + internal bool HasNames { get { return !_parameterNames.IsDefault; } } + + internal bool HasTypes { get { return !_parameterTypesWithAnnotations.IsDefault; } } + public override bool HasSignature { get { return !_parameterNames.IsDefault; } } public override bool HasExplicitlyTypedParameterList { get { return !_parameterTypesWithAnnotations.IsDefault; } } @@ -1142,7 +1145,7 @@ public override Location ParameterLocation(int index) public override string ParameterName(int index) { Debug.Assert(!_parameterNames.IsDefault && 0 <= index && index < _parameterNames.Length); - return _parameterNames[index]; + return _parameterNames.IsDefault ? null : _parameterNames[index]; } public override bool ParameterIsDiscard(int index) @@ -1160,7 +1163,7 @@ public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) { Debug.Assert(this.HasExplicitlyTypedParameterList); Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length); - return _parameterTypesWithAnnotations[index]; + return _parameterTypesWithAnnotations.IsDefault ? default : _parameterTypesWithAnnotations[index]; } protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics) diff --git a/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs b/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs index 80b41dbcbfc3e..e126db2d4a772 100644 --- a/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs +++ b/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs @@ -570,6 +570,25 @@ void M(int y) }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)] + public async Task UnusedLambdaParameter_DiscardTwo() + { + await TestDiagnosticMissingAsync( +@"using System; + +class C +{ + void M(int y) + { + Action myLambda = ([|_|], _) => + { + }; + + myLambda(y, y); + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)] public async Task UsedLocalFunctionParameter() { From 63bbb77ef4d1585ff752899bd17173a492d0d49d Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 24 Sep 2019 16:10:32 -0700 Subject: [PATCH 09/27] Add test for ref/out discard parameters --- .../Semantics/LambdaDiscardParametersTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index a9cd552173d30..f809244cbe70a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -112,6 +112,30 @@ public static void Main() CompileAndVerify(comp, expectedOutput: "356"); } + [Fact] + public void DiscardParameters_RefAndOut() + { + var comp = CreateCompilation(@" +class C +{ + delegate int RefAndOut(ref int i, out int j); + static void M() + { + RefAndOut f1 = (ref int _, out int _) => + { + return 2; + }; + } +}"); + + // Note: this is somewhat problematic because there is nothing the user can do to fix this. We could have an error for out discards + comp.VerifyDiagnostics( + // (9,17): error CS0177: The out parameter '_' must be assigned to before control leaves the current method + // return 2; + Diagnostic(ErrorCode.ERR_ParamUnassigned, "return 2;").WithArguments("_").WithLocation(9, 17) + ); + } + [Fact] public void DiscardParameters_OnLocalFunction() { From 161d3b82305ec05db71ec7603fe878f6ffce4c4b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 22 Oct 2019 11:17:49 -0700 Subject: [PATCH 10/27] Address PR feedback and test plan ideas --- .../Portable/CSharpResources.Designer.cs | 9 ++++ .../CSharp/Portable/CSharpResources.resx | 3 ++ .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../DiagnosticsPass_ExpressionTrees.cs | 4 ++ .../Symbols/Wrapped/WrappedParameterSymbol.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 5 +++ .../Portable/xlf/CSharpResources.de.xlf | 5 +++ .../Portable/xlf/CSharpResources.es.xlf | 5 +++ .../Portable/xlf/CSharpResources.fr.xlf | 5 +++ .../Portable/xlf/CSharpResources.it.xlf | 5 +++ .../Portable/xlf/CSharpResources.ja.xlf | 5 +++ .../Portable/xlf/CSharpResources.ko.xlf | 5 +++ .../Portable/xlf/CSharpResources.pl.xlf | 5 +++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 +++ .../Portable/xlf/CSharpResources.ru.xlf | 5 +++ .../Portable/xlf/CSharpResources.tr.xlf | 5 +++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 +++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 +++ .../Semantics/LambdaDiscardParametersTests.cs | 44 +++++++++++++++++++ 19 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index ce680305d3e29..be627fb964061 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -4551,6 +4551,15 @@ internal static string ERR_ExpressionOrDeclarationExpected { } } + /// + /// Looks up a localized string similar to Expression tree cannot contain lambda discard parameters.. + /// + internal static string ERR_ExpressionTreeCantContainLambdaDiscardParameters { + get { + return ResourceManager.GetString("ERR_ExpressionTreeCantContainLambdaDiscardParameters", resourceCulture); + } + } + /// /// Looks up a localized string similar to An expression tree may not contain a null coalescing assignment. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 87d4118af63eb..332f6e5707b18 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5771,6 +5771,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Expression tree cannot contain value of ref struct or restricted type '{0}'. + + Expression tree cannot contain lambda discard parameters. + 'else' cannot start a statement. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index aa2a5bb92963a..9e6e749276b89 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1736,6 +1736,7 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 8.0 ERR_InternalError = 8751, + ERR_ExpressionTreeCantContainLambdaDiscardParameters = 8752, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 150be15237ceb..1adc325437396 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -431,6 +431,10 @@ public override BoundNode VisitLambda(BoundLambda node) { _diagnostics.Add(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, p.Locations[0], p.Type.Name); } + if (p.IsDiscard) + { + _diagnostics.Add(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, p.Locations[0]); + } } switch (node.Syntax.Kind()) diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs index 14fd139e5d7ba..9e5501540e8a3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs @@ -30,7 +30,7 @@ protected WrappedParameterSymbol(ParameterSymbol underlyingParameter) public ParameterSymbol UnderlyingParameter => _underlyingParameter; - public sealed override bool IsDiscard => false; + public sealed override bool IsDiscard => _underlyingParameter.IsDiscard; #region Forwarded diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index c6c1f8380ccee..ce22672bee140 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -157,6 +157,11 @@ Daný výraz nelze použít v příkazu fixed. + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Strom výrazu nesmí obsahovat přiřazení představující sloučení s hodnotou null. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 05cf96985ae8b..54df359e81d0f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -157,6 +157,11 @@ Der angegebene Ausdruck kann nicht in einer fixed-Anweisung verwendet werden. + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Eine Ausdrucksstruktur darf keine NULL-Zusammenfügungszuweisung enthalten. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 16f12b519f989..b063041e7b5ce 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -157,6 +157,11 @@ La expresión proporcionada no se puede utilizar en una declaración fija + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Un árbol de expresión no puede contener una asignación de fusión nula. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 4633e814b6ad7..8abcc5f27e16e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -157,6 +157,11 @@ Impossible d'utiliser l'expression donnée dans une instruction fixed + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Une arborescence de l'expression ne peut pas contenir d'assignation de fusion ayant une valeur null diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index a416d3ef870bf..8cc2aa808b4d1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -157,6 +157,11 @@ Non è possibile usare l'espressione specificata in un'istruzione fixed + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Un albero delle espressioni non può contenere un'espressione Null di coalescenza diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index fb268e7ea6abb..15540e9b81465 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -157,6 +157,11 @@ 指定された式を fixed ステートメントで使用することはできません + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment 式ツリーに null 合体割り当てを含めることはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 545f933b84321..d01f76be59fa1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -157,6 +157,11 @@ fixed 문에서는 지정된 식을 사용할 수 없습니다. + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment 식 트리에는 null 병합 할당을 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 4f448cf06c787..60dc40232e025 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -157,6 +157,11 @@ Podanego wyrażenia nie można użyć w instrukcji fixed + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Drzewo wyrażeń nie może zawierać przypisania łączącego wartość null diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 34c22baa89db9..bfbc68c4183c9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -157,6 +157,11 @@ A expressão determinada não pode ser usada em uma instrução fixa + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Uma árvore de expressão não pode conter uma atribuição de união nula diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 5528e5c46fb66..7d1babc0cb88a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -157,6 +157,11 @@ Заданное выражение невозможно использовать в операторе fixed + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment Дерево выражений не может содержать назначение объединения со значением NULL. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a499f7d76c762..a1b47234ff3d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -157,6 +157,11 @@ Belirtilen ifade, fixed deyiminde kullanılamıyor + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment İfade ağacı, null birleştirme ataması içeremez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index cd58137933b6c..35e6ad6f0f69b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -157,6 +157,11 @@ 给定表达式不能用于 fixed 语句中 + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment 表达式树可能不包含空的合并赋值 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 2c04cbcb911fb..6de4dff5a9449 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -157,6 +157,11 @@ 指定運算式無法用於 fixed 陳述式中 + + Expression tree cannot contain lambda discard parameters. + Expression tree cannot contain lambda discard parameters. + + An expression tree may not contain a null coalescing assignment 運算式樹狀結構不可包含 null 聯合指派 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index f809244cbe70a..9ee27e0a6e376 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -181,6 +181,50 @@ public static void Main() ); } + [Fact] + public void DiscardParameters_EscapedUnderscore() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (@_, @_) => 3L; + @_ = 1; + } +}"); + comp.VerifyDiagnostics( + // (6,51): error CS0100: The parameter name '_' is a duplicate + // System.Func f1 = (@_, @_) => 3L; + Diagnostic(ErrorCode.ERR_DuplicateParamName, "@_").WithArguments("_").WithLocation(6, 51), + // (7,9): error CS0103: The name '_' does not exist in the current context + // @_ = 1; + Diagnostic(ErrorCode.ERR_NameNotInContext, "@_").WithArguments("_").WithLocation(7, 9) + ); + } + + [Fact] + public void DiscardParameters_ExpressionTreeNotAllowed() + { + var c = CreateCompilation(@" +using System; +using System.Linq.Expressions; +class C +{ + void M() + { + Expression> e = (_, _) => null; + } +}"); + c.VerifyDiagnostics( + // (8,49): error CS8752: Expression tree cannot contain lambda discard parameters. + // Expression> e = (_, _) => null; + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, "_").WithLocation(8, 49), + // (8,52): error CS8752: Expression tree cannot contain lambda discard parameters. + // Expression> e = (_, _) => null; + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, "_").WithLocation(8, 52)); + } + [Fact] public void DiscardParameters_WithTypes() { From 658fd210e0917e0637a0ad9bb26d9ea0bfab747e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 22 Oct 2019 17:32:40 -0700 Subject: [PATCH 11/27] Allow in expression trees --- .../Portable/CSharpResources.Designer.cs | 9 -- .../CSharp/Portable/CSharpResources.resx | 3 - .../CSharp/Portable/Errors/ErrorCode.cs | 1 - .../DiagnosticsPass_ExpressionTrees.cs | 4 - .../Portable/xlf/CSharpResources.cs.xlf | 5 - .../Portable/xlf/CSharpResources.de.xlf | 5 - .../Portable/xlf/CSharpResources.es.xlf | 5 - .../Portable/xlf/CSharpResources.fr.xlf | 5 - .../Portable/xlf/CSharpResources.it.xlf | 5 - .../Portable/xlf/CSharpResources.ja.xlf | 5 - .../Portable/xlf/CSharpResources.ko.xlf | 5 - .../Portable/xlf/CSharpResources.pl.xlf | 5 - .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 - .../Portable/xlf/CSharpResources.ru.xlf | 5 - .../Portable/xlf/CSharpResources.tr.xlf | 5 - .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 - .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 - .../Emit/CodeGen/CodeGenExprLambdaTests.cs | 40 +++++++ .../Semantics/LambdaDiscardParametersTests.cs | 106 ++++++++++++++---- 19 files changed, 124 insertions(+), 104 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index be627fb964061..ce680305d3e29 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -4551,15 +4551,6 @@ internal static string ERR_ExpressionOrDeclarationExpected { } } - /// - /// Looks up a localized string similar to Expression tree cannot contain lambda discard parameters.. - /// - internal static string ERR_ExpressionTreeCantContainLambdaDiscardParameters { - get { - return ResourceManager.GetString("ERR_ExpressionTreeCantContainLambdaDiscardParameters", resourceCulture); - } - } - /// /// Looks up a localized string similar to An expression tree may not contain a null coalescing assignment. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 332f6e5707b18..87d4118af63eb 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5771,9 +5771,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Expression tree cannot contain value of ref struct or restricted type '{0}'. - - Expression tree cannot contain lambda discard parameters. - 'else' cannot start a statement. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 9e6e749276b89..aa2a5bb92963a 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1736,7 +1736,6 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 8.0 ERR_InternalError = 8751, - ERR_ExpressionTreeCantContainLambdaDiscardParameters = 8752, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 1adc325437396..150be15237ceb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -431,10 +431,6 @@ public override BoundNode VisitLambda(BoundLambda node) { _diagnostics.Add(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, p.Locations[0], p.Type.Name); } - if (p.IsDiscard) - { - _diagnostics.Add(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, p.Locations[0]); - } } switch (node.Syntax.Kind()) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index ce22672bee140..c6c1f8380ccee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -157,11 +157,6 @@ Daný výraz nelze použít v příkazu fixed. - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Strom výrazu nesmí obsahovat přiřazení představující sloučení s hodnotou null. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 54df359e81d0f..05cf96985ae8b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -157,11 +157,6 @@ Der angegebene Ausdruck kann nicht in einer fixed-Anweisung verwendet werden. - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Eine Ausdrucksstruktur darf keine NULL-Zusammenfügungszuweisung enthalten. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b063041e7b5ce..16f12b519f989 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -157,11 +157,6 @@ La expresión proporcionada no se puede utilizar en una declaración fija - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Un árbol de expresión no puede contener una asignación de fusión nula. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 8abcc5f27e16e..4633e814b6ad7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -157,11 +157,6 @@ Impossible d'utiliser l'expression donnée dans une instruction fixed - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Une arborescence de l'expression ne peut pas contenir d'assignation de fusion ayant une valeur null diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8cc2aa808b4d1..a416d3ef870bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -157,11 +157,6 @@ Non è possibile usare l'espressione specificata in un'istruzione fixed - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Un albero delle espressioni non può contenere un'espressione Null di coalescenza diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 15540e9b81465..fb268e7ea6abb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -157,11 +157,6 @@ 指定された式を fixed ステートメントで使用することはできません - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment 式ツリーに null 合体割り当てを含めることはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d01f76be59fa1..545f933b84321 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -157,11 +157,6 @@ fixed 문에서는 지정된 식을 사용할 수 없습니다. - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment 식 트리에는 null 병합 할당을 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 60dc40232e025..4f448cf06c787 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -157,11 +157,6 @@ Podanego wyrażenia nie można użyć w instrukcji fixed - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Drzewo wyrażeń nie może zawierać przypisania łączącego wartość null diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index bfbc68c4183c9..34c22baa89db9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -157,11 +157,6 @@ A expressão determinada não pode ser usada em uma instrução fixa - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Uma árvore de expressão não pode conter uma atribuição de união nula diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 7d1babc0cb88a..5528e5c46fb66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -157,11 +157,6 @@ Заданное выражение невозможно использовать в операторе fixed - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment Дерево выражений не может содержать назначение объединения со значением NULL. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a1b47234ff3d4..a499f7d76c762 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -157,11 +157,6 @@ Belirtilen ifade, fixed deyiminde kullanılamıyor - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment İfade ağacı, null birleştirme ataması içeremez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 35e6ad6f0f69b..cd58137933b6c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -157,11 +157,6 @@ 给定表达式不能用于 fixed 语句中 - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment 表达式树可能不包含空的合并赋值 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 6de4dff5a9449..2c04cbcb911fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -157,11 +157,6 @@ 指定運算式無法用於 fixed 陳述式中 - - Expression tree cannot contain lambda discard parameters. - Expression tree cannot contain lambda discard parameters. - - An expression tree may not contain a null coalescing assignment 運算式樹狀結構不可包含 null 聯合指派 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs index 1640b6c0a7760..ea3602cad6056 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs @@ -1968,6 +1968,46 @@ public static void Main() expectedOutput: TrimExpectedOutput(expectedOutput)); } + [Fact] + public void DiscardParameters() + { + var text = +@"using System; +using System.Linq.Expressions; + +class Test +{ + public static void Main() + { + Expression> e = (_, _) => null; + ExpressionVisitor ev = new ExpressionVisitor(); + ev.Visit(e); + + Console.Write(ev.toStr); + } +}"; + string expectedOutput = @" + Lambda: + Type->System.Func`3[System.Int32,System.Int64,System.String] + Parameters-> + Parameter: + Type->System.Int32 + Name->_ + Parameter: + Type->System.Int64 + Name->_ + Body-> + Constant: + Type->System.String + Value-> +"; + + CompileAndVerifyUtil( + new[] { text, TreeWalkerLib }, + parseOptions: TestOptions.RegularPreview, + expectedOutput: TrimExpectedOutput(expectedOutput)); + } + [WorkItem(544213, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544213")] [Fact] public void DelegateInvocation() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 9ee27e0a6e376..096e0d31c9cf1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -203,28 +203,6 @@ public static void Main() ); } - [Fact] - public void DiscardParameters_ExpressionTreeNotAllowed() - { - var c = CreateCompilation(@" -using System; -using System.Linq.Expressions; -class C -{ - void M() - { - Expression> e = (_, _) => null; - } -}"); - c.VerifyDiagnostics( - // (8,49): error CS8752: Expression tree cannot contain lambda discard parameters. - // Expression> e = (_, _) => null; - Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, "_").WithLocation(8, 49), - // (8,52): error CS8752: Expression tree cannot contain lambda discard parameters. - // Expression> e = (_, _) => null; - Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainLambdaDiscardParameters, "_").WithLocation(8, 52)); - } - [Fact] public void DiscardParameters_WithTypes() { @@ -318,6 +296,90 @@ public static void Main() Assert.Null(model.GetSymbolInfo(underscore).Symbol); } + [Fact] + public void DiscardParameters_NotInScope_BindToOutsideLocal() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + int _ = 0; + System.Func f = (_, _) => _++; + System.Func f2 = (_, a) => _++; + } +}"); + // Note that naming one of the parameters seems irrelevant but results in a binding change + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + Assert.Equal(2, underscores.Length); + + var localSymbol = model.GetSymbolInfo(underscores[0]).Symbol; + Assert.Equal("System.Int32 _", localSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Local, localSymbol.Kind); + + var parameterSymbol = model.GetSymbolInfo(underscores[1]).Symbol; + Assert.Equal("System.Int64 _", parameterSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Parameter, parameterSymbol.Kind); + } + + [Fact] + public void DiscardParameters_NotInScope_DeclareLocalNamedUnderscoreInside() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + System.Func f = (_, _) => { long _ = 0; return _++; }; + System.Func f2 = (_, a) => { long _ = 0; return _++; }; + } +}"); + // Note that naming one of the parameters seems irrelevant but results in a binding change + comp.VerifyDiagnostics( + // (7,65): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // System.Func f2 = (_, a) => { long _ = 0; return _++; }; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(7, 65) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + Assert.Equal(2, underscores.Length); + + var localSymbol = model.GetSymbolInfo(underscores[0]).Symbol; + Assert.Equal("System.Int64 _", localSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Local, localSymbol.Kind); + + var parameterSymbol = model.GetSymbolInfo(underscores[1]).Symbol; + Assert.Equal("System.Int64 _", parameterSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Local, parameterSymbol.Kind); + } + + [Fact] + public void DiscardParameters_NotInScope_Nameof() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + System.Func f = (_, _) => nameof(_); // 1 + System.Func f2 = (_, a) => nameof(_); + System.Func f3 = (_) => nameof(_); + } +}"); + // Note that naming one of the parameters seems irrelevant but results in a binding change + comp.VerifyDiagnostics( + // (6,66): error CS0103: The name '_' does not exist in the current context + // System.Func f = (_, _) => nameof(_); // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(6, 66) + ); + } + [Fact] public void DiscardParameters_NotADiscardWhenSingleUnderscore() { From cadd8dce01a67417332660c3980593b26c5f1bc6 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 24 Oct 2019 10:46:22 -0700 Subject: [PATCH 12/27] Support general discard parameters --- .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../CSharp/Portable/Binder/Binder_Lookup.cs | 5 + .../Portable/Binder/Binder_NameConflicts.cs | 17 + .../CSharp/Portable/Binder/InMethodBinder.cs | 5 +- .../OverloadResolution_ArgsToParameters.cs | 2 +- .../Binder/WithLambdaParametersBinder.cs | 21 +- .../Portable/Binder/WithParametersBinder.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 9 - .../CSharp/Portable/CSharpResources.resx | 4 +- .../Compiler/DocumentationCommentCompiler.cs | 2 +- .../CSharp/Portable/Errors/ErrorCode.cs | 3 + .../CSharp/Portable/Errors/MessageID.cs | 4 +- .../Symbols/Metadata/PE/PEParameterSymbol.cs | 36 +- .../Symbols/Source/ParameterHelpers.cs | 11 + .../Source/SourceComplexParameterSymbol.cs | 8 +- .../Symbols/Source/SourceParameterSymbol.cs | 12 +- .../Source/SourceSimpleParameterSymbol.cs | 5 +- ...SynthesizedAccessorValueParameterSymbol.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 +- .../Portable/xlf/CSharpResources.de.xlf | 10 +- .../Portable/xlf/CSharpResources.es.xlf | 10 +- .../Portable/xlf/CSharpResources.fr.xlf | 10 +- .../Portable/xlf/CSharpResources.it.xlf | 10 +- .../Portable/xlf/CSharpResources.ja.xlf | 10 +- .../Portable/xlf/CSharpResources.ko.xlf | 10 +- .../Portable/xlf/CSharpResources.pl.xlf | 10 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 +- .../Portable/xlf/CSharpResources.ru.xlf | 10 +- .../Portable/xlf/CSharpResources.tr.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 +- .../Semantics/LambdaDiscardParametersTests.cs | 421 +++++++++++++++++- 32 files changed, 578 insertions(+), 123 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 8b2c079762db6..db62c43f367b9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -278,7 +278,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB if (seenDiscard) { // We only report the diagnostic on the second and subsequent underscores - MessageID.IDS_FeatureLambdaDiscardParameters.CheckFeatureAvailability( + MessageID.IDS_FeatureDiscardParameters.CheckFeatureAvailability( diagnostics, binder.Compilation, lambda.ParameterLocation(i)); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 3b782c3ba40f2..00b27747f9744 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1388,6 +1388,11 @@ internal bool CanAddLookupSymbolInfo(Symbol symbol, LookupOptions options, Looku Debug.Assert(options.AreValid()); HashSet useSiteDiagnostics = null; + if (symbol is ParameterSymbol { IsDiscard: true}) + { + return false; + } + var name = aliasSymbol != null ? aliasSymbol.Name : symbol.Name; if (!info.CanBeAdded(name)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs index 5e3a75c5a2cab..3300c5508f873 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs @@ -58,6 +58,8 @@ internal void ValidateParameterNameConflicts( if (!parameters.IsDefaultOrEmpty) { pNames = PooledHashSet.GetInstance(); + bool seenDiscard = false; + foreach (var p in parameters) { var name = p.Name; @@ -72,6 +74,21 @@ internal void ValidateParameterNameConflicts( diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name); } + if (p.IsDiscard) + { + if (seenDiscard) + { + // We only report the diagnostic on the second and subsequent underscores + MessageID.IDS_FeatureDiscardParameters.CheckFeatureAvailability( + diagnostics, + this.Compilation, + p.Locations[0]); + } + + seenDiscard = true; + continue; + } + if (!pNames.Add(name)) { // The parameter name '{0}' is a duplicate diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs index 4ca2ddcddeece..1010333026d0f 100644 --- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs @@ -229,7 +229,10 @@ internal override void LookupSymbolsInSingleBinder( parameterMap = new MultiDictionary(parameters.Length, EqualityComparer.Default); foreach (var parameter in parameters) { - parameterMap.Add(parameter.Name, parameter); + if (!parameter.IsDiscard) + { + parameterMap.Add(parameter.Name, parameter); + } } _lazyParameterMap = parameterMap; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs index e05de2b51fa0a..bc49ea8dc9834 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs @@ -313,7 +313,7 @@ private static ArgumentAnalysisResult AnalyzeArguments( { // p is initialized to zero; it is ok for a named argument to "correspond" to // _any_ parameter (not just the parameters past the point of positional arguments) - if (memberParameters[p].Name == name.Identifier.ValueText) + if (memberParameters[p].Name == name.Identifier.ValueText && !memberParameters[p].IsDiscard) { if (isValidParams && p == memberParameters.Length - 1) { diff --git a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs index 10c4a8f343cc1..b571ef026ca0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs @@ -25,7 +25,7 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) var parameters = lambdaSymbol.Parameters; if (!parameters.IsDefaultOrEmpty) { - RecordDefinitions(parameters); + recordDefinitions(parameters); foreach (var parameter in lambdaSymbol.Parameters) { if (!parameter.IsDiscard) @@ -34,16 +34,16 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) } } } - } - private void RecordDefinitions(ImmutableArray definitions) - { - var declarationMap = _definitionMap ?? (_definitionMap = new SmallDictionary()); - foreach (var s in definitions) + void recordDefinitions(ImmutableArray definitions) { - if (!s.IsDiscard && !declarationMap.ContainsKey(s.Name)) + var declarationMap = _definitionMap ?? (_definitionMap = new SmallDictionary()); + foreach (var s in definitions) { - declarationMap.Add(s.Name, s); + if (!s.IsDiscard && !declarationMap.ContainsKey(s.Name)) + { + declarationMap.Add(s.Name, s); + } } } } @@ -95,7 +95,10 @@ internal override void LookupSymbolsInSingleBinder( foreach (var parameterSymbol in parameterMap[name]) { - result.MergeEqual(originalBinder.CheckViability(parameterSymbol, arity, options, null, diagnose, ref useSiteDiagnostics)); + if (!parameterSymbol.IsDiscard) + { + result.MergeEqual(originalBinder.CheckViability(parameterSymbol, arity, options, null, diagnose, ref useSiteDiagnostics)); + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs index be1cbcf60a47b..d0422a0d4ff75 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs @@ -49,7 +49,7 @@ internal override void LookupSymbolsInSingleBinder( foreach (ParameterSymbol parameter in _parameters) { - if (parameter.Name == name) + if (parameter.Name == name && !parameter.IsDiscard) { result.MergeEqual(originalBinder.CheckViability(parameter, arity, options, null, diagnose, ref useSiteDiagnostics)); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index ce680305d3e29..800ff3a621dda 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -11455,15 +11455,6 @@ internal static string IDS_FeatureLambda { } } - /// - /// Looks up a localized string similar to lambda discard parameters. - /// - internal static string IDS_FeatureLambdaDiscardParameters { - get { - return ResourceManager.GetString("IDS_FeatureLambdaDiscardParameters", resourceCulture); - } - } - /// /// Looks up a localized string similar to leading digit separator. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 87d4118af63eb..5e0dbebe93b6c 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5744,8 +5744,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ name shadowing in nested functions - - lambda discard parameters + + discard parameters Cannot use a collection of dynamic type in an asynchronous foreach diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index 620ea5f32528f..70a7a1640e813 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -342,7 +342,7 @@ public override void DefaultVisit(Symbol symbol) { foreach (ParameterSymbol parameter in GetParameters(symbol)) { - if (!documentedParameters.Contains(parameter)) + if (!parameter.IsDiscard && !documentedParameters.Contains(parameter)) { Location location = parameter.Locations[0]; Debug.Assert(location.SourceTree.ReportDocumentationCommentDiagnostics()); //Should be the same tree as for the symbol. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index aa2a5bb92963a..e2861166469e1 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1736,6 +1736,9 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 8.0 ERR_InternalError = 8751, + ERR_LocalIllegallyOverridesDiscardParameter = 8752, + ERR_CannotReferenceDiscards = 8753, + ERR_DiscardNamedArgument = 8754, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 890829783d39c..d0abd8b6466cc 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -182,7 +182,7 @@ internal enum MessageID IDS_FeatureNestedStackalloc = MessageBase + 12762, IDS_FeatureSwitchExpression = MessageBase + 12763, IDS_FeatureAsyncUsing = MessageBase + 12764, - IDS_FeatureLambdaDiscardParameters = MessageBase + 12765, + IDS_FeatureDiscardParameters = MessageBase + 12765, } // Message IDs may refer to strings that need to be localized. @@ -291,7 +291,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) switch (feature) { // Preview features. - case MessageID.IDS_FeatureLambdaDiscardParameters: + case MessageID.IDS_FeatureDiscardParameters: return LanguageVersion.Preview; // C# 8.0 features. diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index dee10b68bc992..f9968e58bca81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -289,7 +289,41 @@ private PEParameterSymbol( private bool HasNameInMetadata => _packedFlags.HasNameInMetadata; - public sealed override bool IsDiscard => false; + private const string underscore = "_"; + + public sealed override bool IsDiscard + { + get + { + if (Name != underscore) + { + return false; + } + + var parameters = _containingSymbol switch + { + PEMethodSymbol method => method.Parameters, + PEPropertySymbol property => property.Parameters, + _ => throw ExceptionUtilities.UnexpectedValue(_containingSymbol.Kind) + }; + + int underscoresCount = 0; + foreach (var p in parameters) + { + if (p.Name == underscore) + { + underscoresCount++; + + if (underscoresCount >= 2) + { + return true; + } + } + } + + return false; + } + } private static PEParameterSymbol Create( PEModuleSymbol moduleSymbol, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 2e546b3746b4c..a21e270e5dc6e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -29,6 +29,15 @@ public static ImmutableArray MakeParameters( var builder = ArrayBuilder.GetInstance(); var mustBeLastParameter = (ParameterSyntax)null; + int discardsCount = 0; + foreach (var parameterSyntax in syntax.Parameters) + { + if (parameterSyntax.Identifier.IsUnderscoreToken()) + { + discardsCount++; + } + } + foreach (var parameterSyntax in syntax.Parameters) { if (mustBeLastParameter == null) @@ -79,6 +88,7 @@ public static ImmutableArray MakeParameters( diagnostics.Add(ErrorCode.ERR_IllegalRefParam, refnessKeyword.GetLocation()); } + bool isDiscard = discardsCount >= 2 && parameterSyntax.Identifier.IsUnderscoreToken(); var parameter = SourceParameterSymbol.Create( binder, owner, @@ -90,6 +100,7 @@ public static ImmutableArray MakeParameters( (paramsKeyword.Kind() != SyntaxKind.None), parameterIndex == 0 && thisKeyword.Kind() != SyntaxKind.None, addRefReadOnlyModifier, + isDiscard, diagnostics); ReportParameterErrors(owner, parameterSyntax, parameter, thisKeyword, paramsKeyword, firstDefault, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index ccdfa26fbec68..190af913513b8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -38,12 +38,13 @@ internal SourceComplexParameterSymbol( TypeWithAnnotations parameterType, RefKind refKind, string name, + bool isDiscard, ImmutableArray locations, SyntaxReference syntaxRef, ConstantValue defaultSyntaxValue, bool isParams, bool isExtensionMethodThis) - : base(owner, parameterType, ordinal, refKind, name, locations) + : base(owner, parameterType, ordinal, refKind, name, isDiscard, locations) { Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter))); @@ -77,8 +78,6 @@ internal SourceComplexParameterSymbol( internal SyntaxTree SyntaxTree => _syntaxRef == null ? null : _syntaxRef.SyntaxTree; - public sealed override bool IsDiscard => false; - internal override ConstantValue ExplicitDefaultConstantValue { get @@ -1149,12 +1148,13 @@ internal SourceComplexParameterSymbolWithCustomModifiersPrecedingByRef( RefKind refKind, ImmutableArray refCustomModifiers, string name, + bool isDiscard, ImmutableArray locations, SyntaxReference syntaxRef, ConstantValue defaultSyntaxValue, bool isParams, bool isExtensionMethodThis) - : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, defaultSyntaxValue, isParams, isExtensionMethodThis) + : base(owner, ordinal, parameterType, refKind, name, isDiscard, locations, syntaxRef, defaultSyntaxValue, isParams, isExtensionMethodThis) { Debug.Assert(!refCustomModifiers.IsEmpty); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 2ebe74b3a9a63..4c998fca3d41e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -19,6 +19,7 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase protected SymbolCompletionState state; protected readonly TypeWithAnnotations parameterType; private readonly string _name; + private readonly bool _isDiscard; private readonly ImmutableArray _locations; private readonly RefKind _refKind; @@ -33,6 +34,7 @@ public static SourceParameterSymbol Create( bool isParams, bool isExtensionMethodThis, bool addRefReadOnlyModifier, + bool isDiscard, DiagnosticBag declarationDiagnostics) { var name = identifier.ValueText; @@ -58,6 +60,7 @@ public static SourceParameterSymbol Create( refKind, ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)), name, + isDiscard, locations, syntax.GetReference(), ConstantValue.Unset, @@ -71,7 +74,7 @@ public static SourceParameterSymbol Create( (syntax.AttributeLists.Count == 0) && !owner.IsPartialMethod()) { - return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, isDiscard: false, locations); + return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, isDiscard, locations); } return new SourceComplexParameterSymbol( @@ -80,6 +83,7 @@ public static SourceParameterSymbol Create( parameterType, refKind, name, + isDiscard, locations, syntax.GetReference(), ConstantValue.Unset, @@ -93,6 +97,7 @@ protected SourceParameterSymbol( int ordinal, RefKind refKind, string name, + bool isDiscard, ImmutableArray locations) : base(owner, ordinal) { @@ -106,6 +111,7 @@ protected SourceParameterSymbol( this.parameterType = parameterType; _refKind = refKind; _name = name; + _isDiscard = isDiscard; _locations = locations; } @@ -128,6 +134,7 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy newTypeWithModifiers, _refKind, _name, + _isDiscard, _locations, this.SyntaxReference, this.ExplicitDefaultConstantValue, @@ -145,6 +152,7 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy _refKind, newRefCustomModifiers, _name, + _isDiscard, _locations, this.SyntaxReference, this.ExplicitDefaultConstantValue, @@ -203,6 +211,8 @@ internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics) internal abstract bool IsExtensionMethodThis { get; } + public sealed override bool IsDiscard => _isDiscard; + public sealed override RefKind RefKind { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index f0630f383f910..7b8e3296ed334 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -19,13 +19,10 @@ public SourceSimpleParameterSymbol( string name, bool isDiscard, ImmutableArray locations) - : base(owner, parameterType, ordinal, refKind, name, locations) + : base(owner, parameterType, ordinal, refKind, name, isDiscard, locations) { - IsDiscard = isDiscard; } - public override bool IsDiscard { get; } - internal override ConstantValue ExplicitDefaultConstantValue { get { return null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs index 3110d6ec34148..786cb715b99df 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal sealed class SynthesizedAccessorValueParameterSymbol : SourceComplexParameterSymbol { public SynthesizedAccessorValueParameterSymbol(SourceMemberMethodSymbol accessor, TypeWithAnnotations paramType, int ordinal) - : base(accessor, ordinal, paramType, RefKind.None, ParameterSymbol.ValueParameterName, accessor.Locations, + : base(accessor, ordinal, paramType, RefKind.None, ParameterSymbol.ValueParameterName, isDiscard: false, accessor.Locations, syntaxRef: null, defaultSyntaxValue: ConstantValue.Unset, // the default value can be set via [param: DefaultParameterValue] applied on the accessor isParams: false, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index c6c1f8380ccee..e7117a134f60f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -814,6 +814,11 @@ delegovat obecná omezení typu + + discard parameters + discard parameters + + enum generic type constraints výčet obecných omezení typu @@ -839,11 +844,6 @@ indexování mobilních vyrovnávacích pamětí pevné velikosti - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions skrývání názvů ve vnořených funkcích diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 05cf96985ae8b..d8909816fac34 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -814,6 +814,11 @@ Generische Typeneinschränkungen für Delegat + + discard parameters + discard parameters + + enum generic type constraints Generische Typeneinschränkungen für Enumeration @@ -839,11 +844,6 @@ Bewegliche Puffer fester Größe werden indiziert. - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions Namensshadowing in geschachtelten Funktionen diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 16f12b519f989..35b19b8577bf2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -815,6 +815,11 @@ restricciones de tipo genérico delegate + + discard parameters + discard parameters + + enum generic type constraints restricciones de tipo genérico enum @@ -840,11 +845,6 @@ indexando búferes fijos movibles - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions sombreado de nombres en funciones anidadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 4633e814b6ad7..ed5afe11e02b8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -814,6 +814,11 @@ contraintes de type générique de délégué + + discard parameters + discard parameters + + enum generic type constraints contraintes de type générique d'enum @@ -839,11 +844,6 @@ indexation de mémoires tampons fixes mobiles - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions ombrage des noms dans les fonctions imbriquées diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index a416d3ef870bf..3b816273193f5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -814,6 +814,11 @@ vincoli di tipo generico delegato + + discard parameters + discard parameters + + enum generic type constraints vincoli di tipo generico enumerazione @@ -839,11 +844,6 @@ indicizzazione di buffer fissi mobili - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions shadowing dei nomi nelle funzioni annidate diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index fb268e7ea6abb..600531557050d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -814,6 +814,11 @@ delegate ジェネリック型の制約 + + discard parameters + discard parameters + + enum generic type constraints enum ジェネリック型の制約 @@ -839,11 +844,6 @@ 移動可能な固定バッファーのインデックス化 - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions 入れ子になった関数での名前シャドウイング diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 545f933b84321..7b9a766d03650 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -814,6 +814,11 @@ 대리자 제네릭 형식 제약 조건 + + discard parameters + discard parameters + + enum generic type constraints 열거형 제네릭 형식 제약 조건 @@ -839,11 +844,6 @@ 이동 가능한 고정 버퍼 인덱싱 - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions 중첩된 함수의 이름 섀도잉 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 4f448cf06c787..cd892b2865e26 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -814,6 +814,11 @@ ogólne ograniczenia typów delegowania + + discard parameters + discard parameters + + enum generic type constraints ogólne ograniczenia typów wyliczenia @@ -839,11 +844,6 @@ indeksowanie możliwych do przenoszenia buforów fixed - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions zasłanianie nazw w funkcjach zagnieżdżonych diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 34c22baa89db9..7c22ff829cd53 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -814,6 +814,11 @@ restrições de tipo genérico delegate + + discard parameters + discard parameters + + enum generic type constraints restrições de tipo genérico enum @@ -839,11 +844,6 @@ buffers fixos móveis de indexação - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions sombreamento de nome em funções aninhadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 5528e5c46fb66..d14505c910509 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -814,6 +814,11 @@ ограничения универсального типа для делегата + + discard parameters + discard parameters + + enum generic type constraints ограничения универсального типа перечисления @@ -839,11 +844,6 @@ индексирование перемещаемых буферов фиксированного размера - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions скрытие имен во вложенных функциях diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a499f7d76c762..bd5fcbd50da4c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -814,6 +814,11 @@ delegate genel tür kısıtlamaları + + discard parameters + discard parameters + + enum generic type constraints enum genel tür kısıtlamaları @@ -839,11 +844,6 @@ taşınabilir sabit arabellekler dizine alınıyor - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions iç içe işlevlerde ad gölgeleme diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index cd58137933b6c..407379b591b14 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -814,6 +814,11 @@ 委托泛型类型约束 + + discard parameters + discard parameters + + enum generic type constraints 枚举泛型类型约束 @@ -839,11 +844,6 @@ 正在编制可移动固定缓冲区的索引 - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions 在嵌套函数中的名称映射 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 2c04cbcb911fb..ccf9afbd8d6ca 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -814,6 +814,11 @@ 委派泛型類型條件約束 + + discard parameters + discard parameters + + enum generic type constraints 列舉泛型類型條件約束 @@ -839,11 +844,6 @@ 對可移動的固定緩衝區編製索引 - - lambda discard parameters - lambda discard parameters - - name shadowing in nested functions 巢狀函式中的名稱鏡像處理 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 096e0d31c9cf1..3669921d9777c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -51,24 +51,24 @@ public static void Main() }", parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( - // (6,51): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (6,51): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // System.Func f1 = (_, _) => 3L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(6, 51), - // (10,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(6, 51), + // (10,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 4L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(10, 13), - // (13,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(10, 13), + // (13,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 5L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(13, 13), - // (16,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(13, 13), + // (16,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _, - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(16, 13), - // (17,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(16, 13), + // (17,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 6L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(17, 13), - // (20,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(17, 13), + // (20,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _, - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(20, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(20, 13) ); var tree = comp.SyntaxTrees.Single(); @@ -144,18 +144,372 @@ class C { static void M() { - local(); - void local(int _, int _) {} + local(1, 2); + void local(int _, int _) { } + } +}"); + + comp.VerifyDiagnostics(); + } + + [Fact] + public void DiscardParameters_OnLocalFunction_NotInScope() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + int _ = 0; + local(1, 2); + void local(int _, int _) { _++; } + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var underscore = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").Single(); + + var localSymbol = model.GetSymbolInfo(underscore).Symbol; + Assert.Equal("System.Int32 _", localSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Local, localSymbol.Kind); + } + + [Fact] + public void DiscardParameters_OnMethod() + { + var comp = CreateCompilation(@" +public class C +{ + public static void M(int _, int _) + { + M(1, 2); + _ = """"; + } +}"); + + comp.VerifyDiagnostics(); + + var comp2 = CreateCompilation(@" +class D +{ + public static void M2() + { + C.M(1, 2); + } +} +", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + + var comp3 = CreateCompilation(@" +class D +{ + public static void M2() + { + C.M(1, _: 2); + C.M(_: 1, 2); + } +} +", references: new[] { comp.EmitToImageReference() }); + comp3.VerifyDiagnostics( + // (6,16): error CS1739: The best overload for 'M' does not have a parameter named '_' + // C.M(1, _: 2); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 16), + // (7,13): error CS1739: The best overload for 'M' does not have a parameter named '_' + // C.M(_: 1, 2); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 13) + ); + } + + [Fact] + public void DiscardParameters_OnMethod_NamedArgument() + { + var comp = CreateCompilation(@" +class C +{ + static void M(int _, string _) + { + M(1, _: null); + M(_: 1, null); } }"); comp.VerifyDiagnostics( - // (6,9): error CS7036: There is no argument given that corresponds to the required formal parameter '_' of 'local(int, int)' - // local(); - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "local").WithArguments("_", "local(int, int)").WithLocation(6, 9), - // (7,31): error CS0100: The parameter name '_' is a duplicate - // void local(int _, int _) {} - Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(7, 31) + // (6,14): error CS1739: The best overload for 'M' does not have a parameter named '_' + // M(1, _: null); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 14), + // (7,11): error CS1739: The best overload for 'M' does not have a parameter named '_' + // M(_: 1, null); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 11) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var calls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Null(model.GetSymbolInfo(calls[0]).Symbol); + Assert.Null(model.GetSymbolInfo(calls[1]).Symbol); + } + + [Fact] + public void DiscardParameters_OnMethod_NamedArgument_Underscore() + { + var comp = CreateCompilation(@" +class C +{ + static void M(int a, string _) + { + M(1, _: null); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var call = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void C.M(System.Int32 a, System.String _)", model.GetSymbolInfo(call).Symbol.ToTestDisplayString()); + } + + [Fact] + public void DiscardParameters_OnMethod_NamedArgument_Underscore2() + { + var comp = CreateCompilation(@" +class C +{ + void M(int a, string _) { } + void M(long _, string _) + { + M(1, _: null); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var call = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void C.M(System.Int32 a, System.String _)", model.GetSymbolInfo(call).Symbol.ToTestDisplayString()); + } + + [Fact] + public void DiscardParameters_OnMethod_NamedArgumentDoesNotMatchDiscard() + { + var comp = CreateCompilation(@" +class C +{ + static void M(int _, string _) + { + M(1, b: null); + } +}"); + + comp.VerifyDiagnostics( + // (6,14): error CS1739: The best overload for 'M' does not have a parameter named 'b' + // M(1, b: null); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "b").WithArguments("M", "b").WithLocation(6, 14) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var call = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetSymbolInfo(call).Symbol); + } + + [Fact] + public void DiscardParameters_OnMethod_WithXmlDoc() + { + var comp = CreateCompilation(@" +class C +{ + /// + /// 1 + /// 2 + void M(int _, int _) + { + } +}", parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + + comp.VerifyDiagnostics( + // (5,22): warning CS1572: XML comment has a param tag for '_', but there is no parameter by that name + // /// 1 + Diagnostic(ErrorCode.WRN_UnmatchedParamTag, "_").WithArguments("_").WithLocation(5, 22), + // (6,22): warning CS1572: XML comment has a param tag for '_', but there is no parameter by that name + // /// 2 + Diagnostic(ErrorCode.WRN_UnmatchedParamTag, "_").WithArguments("_").WithLocation(6, 22) + ); + } + + // TODO2 test as range variables? + + [Fact] + public void DiscardParameters_OnMethod_Overridding() + { + var comp = CreateCompilation(@" +public class Base +{ + public virtual void M(int _, int _) + { + } +} +public class C : Base +{ + public override void M(int _, int _) + { + } +}"); + + comp.VerifyDiagnostics(); + } + + [Fact] + public void DiscardParameters_OnMethod_Overridding_SettingNames() + { + var comp = CreateCompilation(@" +public class Base +{ + public virtual void M(int _, int _) + { + } +} +public class C : Base +{ + public override void M(int a, int b) + { + } +}"); + + comp.VerifyDiagnostics(); + } + + [Fact] + public void DiscardParameters_OnMethod_Overridding_RemovingNames() + { + var comp = CreateCompilation(@" +public class Base +{ + public virtual void M(int a, int b) + { + } +} +public class C : Base +{ + public override void M(int _, int _) + { + } +}"); + + comp.VerifyDiagnostics(); + } + + [Fact] + public void DiscardParameters_OnConstructor() + { + var comp = CreateCompilation(@" +class C +{ + C(int _, string _) + { + new C(1, null); + new C(1, _: null); // 1 + new C(_: 1, null); // 2 + _.ToString(); // 3 + } +}"); + + comp.VerifyDiagnostics( + // (7,18): error CS1739: The best overload for 'C' does not have a parameter named '_' + // new C(1, _: null); // 1 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("C", "_").WithLocation(7, 18), + // (8,15): error CS1739: The best overload for 'C' does not have a parameter named '_' + // new C(_: 1, null); // 2 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("C", "_").WithLocation(8, 15), + // (9,9): error CS0103: The name '_' does not exist in the current context + // _.ToString(); // 3 + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(9, 9) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var calls = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal("C..ctor(System.Int32 _, System.String _)", model.GetSymbolInfo(calls[0]).Symbol.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(calls[1]).Symbol); + Assert.Null(model.GetSymbolInfo(calls[2]).Symbol); + } + + [Fact] + public void DiscardParameters_OnDelegate() + { + var comp = CreateCompilation(@" +class C +{ + delegate void Signature(int _, int _); + + static void M(Signature s) + { + s(1, _: 2); + } +}"); + + comp.VerifyDiagnostics( + // (8,14): error CS1746: The delegate 'C.Signature' does not have a parameter named '_' + // s(1, _: 2); + Diagnostic(ErrorCode.ERR_BadNamedArgumentForDelegateInvoke, "_").WithArguments("C.Signature", "_").WithLocation(8, 14) + ); + } + + [Fact] + public void DiscardParameters_OnIndexer() + { + var comp = CreateCompilation(@" +class C1 +{ + int this[int _, int _] => _++; // 1 +}"); + + comp.VerifyDiagnostics( + // (4,31): error CS0103: The name '_' does not exist in the current context + // int this[int _, int _] => _++; // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(4, 31) + ); + + comp = CreateCompilation(@" +public class C +{ + public int this[int _, int _] => 1; +}"); + + comp.VerifyDiagnostics(); + + var comp2 = CreateCompilation(@" +class D +{ + public static void M2(C c) + { + _ = c[1, 2]; + } +} +", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + + var comp3 = CreateCompilation(@" +class D +{ + public static void M2(C c) + { + _ = c[1, _: 2]; + _ = c[_: 1, 2]; + } +} +", references: new[] { comp.EmitToImageReference() }); + comp3.VerifyDiagnostics( + // (6,18): error CS1739: The best overload for 'this' does not have a parameter named '_' + // _ = c[1, _: 2]; + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("this", "_").WithLocation(6, 18), + // (7,15): error CS1739: The best overload for 'this' does not have a parameter named '_' + // _ = c[_: 1, 2]; + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("this", "_").WithLocation(7, 15) ); } @@ -326,6 +680,33 @@ static void M() Assert.Equal(SymbolKind.Parameter, parameterSymbol.Kind); } + [Fact] + public void DiscardParameters_NotInScope_BindToOutsideLocal_Nested() + { + var comp = CreateCompilation(@" +class C +{ + static void M() + { + int _ = 0; + System.Func f = (_, _) => + { + System.Func f2 = (_, _) => _++; + return f2(null, null); + }; + } +}"); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var underscore = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").Single(); + + var localSymbol = model.GetSymbolInfo(underscore).Symbol; + Assert.Equal("System.Int32 _", localSymbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Local, localSymbol.Kind); + } + [Fact] public void DiscardParameters_NotInScope_DeclareLocalNamedUnderscoreInside() { From 1dd6cd36b9e23779c42e19e9db3f40f4f8a0dc81 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 24 Oct 2019 20:07:24 -0700 Subject: [PATCH 13/27] Add IDE tests --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 27 +++++++++++++ .../RemoveUnusedParametersTests.cs | 38 +++++++++++++++++++ .../ChangeSignatureViewModelTests.vb | 38 +++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 27cdc9b36c7ba..0070d2f72499d 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -2691,6 +2691,33 @@ void M() MainDescription($"({FeaturesResources.discard}) int _")); } + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestMethodDiscardParameter_FirstDiscard() + { + await TestAsync( +@"class C +{ + int M(string $$_, int _) => 1; +}", + MainDescription($"({FeaturesResources.discard}) string _")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestLocalFunctionDiscardParameter_SecondDiscard() + { + await TestAsync( +@"class C +{ + void M() + { + local(null, 0); + + int local(string _, int $$_) => 1; + } +}", + MainDescription($"({FeaturesResources.discard}) int _")); + } + [WorkItem(540871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540871")] [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestLiterals() diff --git a/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs b/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs index e126db2d4a772..30aed0c345a30 100644 --- a/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs +++ b/src/EditorFeatures/CSharpTest/RemoveUnusedParametersAndValues/RemoveUnusedParametersTests.cs @@ -589,6 +589,44 @@ void M(int y) }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)] + public async Task UnusedLocalFunctionParameter_DiscardTwo() + { + await TestDiagnosticMissingAsync( +@"using System; + +class C +{ + void M(int y) + { + void local([|_|], _) + { + } + + local(y, y); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)] + public async Task UnusedMethodParameter_DiscardTwo() + { + await TestDiagnosticMissingAsync( +@"using System; + +class C +{ + void M([|_|], _) + { + } + + void M2(int y) + { + M(y, y); + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedParameters)] public async Task UsedLocalFunctionParameter() { diff --git a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb index 75460bd2d4b8e..11080be2425c3 100644 --- a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb +++ b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb @@ -114,6 +114,44 @@ class MyClass monitor.Detach() End Function + + Public Async Function ReorderParameters_MethodWithTwoDiscardParameters_MoveFirstParameterDown() As Tasks.Task + Dim markup = + + Dim viewModelTestState = Await GetViewModelTestStateAsync(markup, LanguageNames.CSharp) + Dim viewModel = viewModelTestState.ViewModel + VerifyOpeningState(viewModel, "public void M(int _, string _)") + + Dim monitor = New PropertyChangedTestMonitor(viewModel) + monitor.AddExpectation(Function() viewModel.IsOkButtonEnabled) + monitor.AddExpectation(Function() viewModel.SignatureDisplay) + monitor.AddExpectation(Function() viewModel.SignaturePreviewAutomationText) + monitor.AddExpectation(Function() viewModel.AllParameters) + monitor.AddExpectation(Function() viewModel.CanMoveUp) + monitor.AddExpectation(Function() viewModel.MoveUpAutomationText) + monitor.AddExpectation(Function() viewModel.CanMoveDown) + monitor.AddExpectation(Function() viewModel.MoveDownAutomationText) + + viewModel.MoveDown() + + VerifyAlteredState( + viewModelTestState, + monitor, + isOkButtonEnabled:=True, + canMoveUp:=True, + canMoveDown:=False, + permutation:={1, 0}, + signatureDisplay:="public void M(string _, int _)") + + monitor.Detach() + End Function + Public Async Function ReorderParameters_MethodWithTwoNormalParameters_RemoveFirstParameter() As Tasks.Task Dim markup = Date: Fri, 25 Oct 2019 16:36:07 -0700 Subject: [PATCH 14/27] Emit with unspeakable name --- .../Symbols/Metadata/PE/PEParameterSymbol.cs | 42 +--- .../Source/SourceClonedParameterSymbol.cs | 2 + .../Source/SourceComplexParameterSymbol.cs | 17 +- .../Symbols/Source/SourceParameterSymbol.cs | 2 + .../Source/SourceSimpleParameterSymbol.cs | 5 + .../Semantics/LambdaDiscardParametersTests.cs | 211 +++++++++++++++++- .../Test/Semantic/Semantics/LambdaTests.cs | 6 +- 7 files changed, 245 insertions(+), 40 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index f9968e58bca81..9bf6dccfeb896 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -287,41 +287,11 @@ private PEParameterSymbol( Debug.Assert(hasNameInMetadata == this.HasNameInMetadata); } - private bool HasNameInMetadata => _packedFlags.HasNameInMetadata; - - private const string underscore = "_"; - - public sealed override bool IsDiscard + private bool HasNameInMetadata { get { - if (Name != underscore) - { - return false; - } - - var parameters = _containingSymbol switch - { - PEMethodSymbol method => method.Parameters, - PEPropertySymbol property => property.Parameters, - _ => throw ExceptionUtilities.UnexpectedValue(_containingSymbol.Kind) - }; - - int underscoresCount = 0; - foreach (var p in parameters) - { - if (p.Name == underscore) - { - underscoresCount++; - - if (underscoresCount >= 2) - { - return true; - } - } - } - - return false; + return _packedFlags.HasNameInMetadata; } } @@ -439,6 +409,14 @@ public override int Ordinal } } + public override bool IsDiscard + { + get + { + return false; + } + } + // might be Nil internal ParameterHandle Handle { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs index ab9db68a26ef5..1ef6d0dfbae8a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs @@ -31,6 +31,8 @@ internal SourceClonedParameterSymbol(SourceParameterSymbol originalParam, Symbol public override bool IsDiscard => _originalParam.IsDiscard; + public override string MetadataName => _originalParam.MetadataName; + public override ImmutableArray DeclaringSyntaxReferences { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 190af913513b8..a9fb3f9874c2e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -316,18 +316,27 @@ public override string MetadataName // The metadata parameter name should be the name used in the partial definition. var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol; - if ((object)sourceMethod == null) + if (sourceMethod is null) { - return base.MetadataName; + return baseMetadataNameOrDiscard(); } var definition = sourceMethod.SourcePartialDefinition; - if ((object)definition == null) + if (definition is null) { - return base.MetadataName; + return baseMetadataNameOrDiscard(); } return definition.Parameters[this.Ordinal].MetadataName; + + string baseMetadataNameOrDiscard() + { + if (IsDiscard) + { + return DiscardMetadataName(Ordinal); + } + return base.MetadataName; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 4c998fca3d41e..02c5b9fffb92f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -213,6 +213,8 @@ internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics) public sealed override bool IsDiscard => _isDiscard; + protected static string DiscardMetadataName(int ordinal) => $"<>_{ordinal + 1}"; + public sealed override RefKind RefKind { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index 7b8e3296ed334..096b80fc9944c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -23,6 +23,11 @@ public SourceSimpleParameterSymbol( { } + public override string MetadataName + { + get { return IsDiscard ? DiscardMetadataName(Ordinal) : base.MetadataName; } + } + internal override ConstantValue ExplicitDefaultConstantValue { get { return null; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 3669921d9777c..4f3a6e34e1e1c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -1,9 +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.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests @@ -72,7 +74,55 @@ public static void Main() ); var tree = comp.SyntaxTrees.Single(); - var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.Identifier.ToString() == "_").ToArray(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); + VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); + } + + [Fact] + public void DiscardParameters_CSharp8_LocalFunctions() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + long f1(short _, string _) => 3L; + System.Console.WriteLine(f1(1, null)); + } +}", parseOptions: TestOptions.Regular8); + + comp.VerifyDiagnostics( + // (6,33): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // long f1(short _, string _) => 3L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(6, 33) + ); + + var tree = comp.SyntaxTrees.Single(); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.Identifier.ToString() == "_").ToArray(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); + VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); + } + + [Fact] + public void DiscardParameters_CSharp8_Methods() + { + var comp = CreateCompilation(@" +public class C +{ + public long M(short _, string _) => 3L; +}", parseOptions: TestOptions.Regular8); + + comp.VerifyDiagnostics( + // (4,35): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public long M(short _, string _) => 3L; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(4, 35) + ); + + var tree = comp.SyntaxTrees.Single(); + var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.Identifier.ToString() == "_").ToArray(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); @@ -200,6 +250,54 @@ public static void M2() C.M(1, 2); } } +", references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics(); + var method = comp2.GlobalNamespace.GetMember("C.M"); + Assert.Equal("void C.M(System.Int32 <>_1, System.Int32 <>_2)", method.ToTestDisplayString()); + + var comp3 = CreateCompilation(@" +class D +{ + public static void M2() + { + C.M(1, _: 2); + C.M(_: 1, 2); + } +} +", references: new[] { comp.EmitToImageReference() }); + comp3.VerifyDiagnostics( + // (6,16): error CS1739: The best overload for 'M' does not have a parameter named '_' + // C.M(1, _: 2); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 16), + // (7,13): error CS1739: The best overload for 'M' does not have a parameter named '_' + // C.M(_: 1, 2); + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 13) + ); + } + + [Fact] + public void DiscardParameters_OnMethod_Partial() + { + var comp = CreateCompilation(@" +public class C +{ + public static void M(int _, int _) + { + M(1, 2); + _ = """"; + } +}"); + + comp.VerifyDiagnostics(); + + var comp2 = CreateCompilation(@" +class D +{ + public static void M2() + { + C.M(1, 2); + } +} ", references: new[] { comp.EmitToImageReference() }); comp2.VerifyDiagnostics(); @@ -459,6 +557,114 @@ static void M(Signature s) ); } + [Fact] + public void DiscardParameters_VerifyMetadata() + { + var comp = CreateCompilation(@" +public class C +{ + public delegate int Delegate(string _, string _); + public int this[string _, string _] => throw null; + public int M1(string _, string _) => throw null; + public int M2(int a, string _, string _) => throw null; + public int M3(string _, int b, string _) => throw null; + public int M4(string _, string _, int c) => throw null; + public int M5(int a, string _, string _ = null) => throw null; +} + +public interface I +{ + int M(int _, string b, int _); +} +"); + comp.VerifyDiagnostics(); + + var comp2 = CreateCompilation("", new[] { comp.EmitToImageReference() }); + var cMembers = comp2.GetTypeByMetadataName("C").GetMembers(); + AssertEx.Equal(new[] { + "System.Int32 C.this[System.String <>_1, System.String <>_2].get", + "System.Int32 C.M1(System.String <>_1, System.String <>_2)", + "System.Int32 C.M2(System.Int32 a, System.String <>_2, System.String <>_3)", + "System.Int32 C.M3(System.String <>_1, System.Int32 b, System.String <>_3)", + "System.Int32 C.M4(System.String <>_1, System.String <>_2, System.Int32 c)", + "System.Int32 C.M5(System.Int32 a, System.String <>_2, [System.String <>_3 = null])", + "C..ctor()", + "System.Int32 C.this[System.String <>_1, System.String <>_2] { get; }", + "C.Delegate" }, + cMembers.Select(m => m.ToTestDisplayString())); + + var iMembers = comp2.GetTypeByMetadataName("I").GetMembers(); + AssertEx.Equal(new[] { + "System.Int32 I.M(System.Int32 <>_1, System.String b, System.Int32 <>_3)" }, + iMembers.Select(m => m.ToTestDisplayString())); + + var delegateMembers = cMembers.OfType().Single().GetMembers(); + AssertEx.Equal(new[] { + "C.Delegate..ctor(System.Object @object, System.IntPtr method)", + "System.Int32 C.Delegate.Invoke(System.String <>_1, System.String <>_2)", + "System.IAsyncResult C.Delegate.BeginInvoke(System.String <>_1, System.String <>_2, System.AsyncCallback callback, System.Object @object)", + "System.Int32 C.Delegate.EndInvoke(System.IAsyncResult result)" }, + delegateMembers.Select(m => m.ToTestDisplayString())); + } + + [Fact] + public void DiscardParameters_VerifyMetadata_OnPartialMethod() + { + var comp = CreateCompilation(@" +public partial class C +{ + partial void M1(string _, string _); + partial void M2(string a, string b); + partial void M3(string _, string _); + partial void M4(string _, string _ = null); +} +public partial class C +{ + partial void M1(string _, string _) => throw null; + partial void M2(string _, string _) => throw null; + partial void M3(string a, string b) => throw null; + partial void M4(string _, string _) => throw null; + + void M() + { + M1(null, null); + + M2(null, null); + M2(a: null, null); + M2(null, b: null); + M2(_: null, null); // 1 + M2(null, _: null); // 2 + + M3(null, null); + M3(a: null, null); // 3 + M3(null, b: null); // 4 + M3(_: null, null); // 5 + M3(null, _: null); // 6 + } +} +"); + comp.VerifyDiagnostics( + // (23,12): error CS1739: The best overload for 'M2' does not have a parameter named '_' + // M2(_: null, null); // 1 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M2", "_").WithLocation(23, 12), + // (24,18): error CS1739: The best overload for 'M2' does not have a parameter named '_' + // M2(null, _: null); // 2 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M2", "_").WithLocation(24, 18), + // (27,12): error CS1739: The best overload for 'M3' does not have a parameter named 'a' + // M3(a: null, null); // 3 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "a").WithArguments("M3", "a").WithLocation(27, 12), + // (28,18): error CS1739: The best overload for 'M3' does not have a parameter named 'b' + // M3(null, b: null); // 4 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "b").WithArguments("M3", "b").WithLocation(28, 18), + // (29,12): error CS1739: The best overload for 'M3' does not have a parameter named '_' + // M3(_: null, null); // 5 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M3", "_").WithLocation(29, 12), + // (30,18): error CS1739: The best overload for 'M3' does not have a parameter named '_' + // M3(null, _: null); // 6 + Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M3", "_").WithLocation(30, 18) + ); + } + [Fact] public void DiscardParameters_OnIndexer() { @@ -493,6 +699,9 @@ public static void M2(C c) ", references: new[] { comp.EmitToImageReference() }); comp2.VerifyDiagnostics(); + var getter = comp2.GetTypeByMetadataName("C").GetMembers().OfType().Where(m => m.Name == "get_Item").Single(); + Assert.Equal("System.Int32 C.this[System.Int32 <>_1, System.Int32 <>_2].get", getter.ToTestDisplayString()); + var comp3 = CreateCompilation(@" class D { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index db585e6ea26eb..13000f5620a84 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -3339,9 +3339,9 @@ static void M() void verifyDiagnostics() { comp.VerifyDiagnostics( - // (8,37): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // Func f = (_, _) => 0; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(8, 37)); + // (8,37): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func f = (_, _) => 0; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(8, 37)); } } From d30ee65e4500e3c113cb331105d1524be8d2926f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 25 Oct 2019 18:43:50 -0700 Subject: [PATCH 15/27] Remove unused error code --- src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs | 2 +- src/Compilers/CSharp/Portable/Errors/ErrorCode.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 00b27747f9744..441f44174b228 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1388,7 +1388,7 @@ internal bool CanAddLookupSymbolInfo(Symbol symbol, LookupOptions options, Looku Debug.Assert(options.AreValid()); HashSet useSiteDiagnostics = null; - if (symbol is ParameterSymbol { IsDiscard: true}) + if (symbol is ParameterSymbol { IsDiscard: true }) { return false; } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index e2861166469e1..aa2a5bb92963a 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1736,9 +1736,6 @@ internal enum ErrorCode #endregion diagnostics introduced for C# 8.0 ERR_InternalError = 8751, - ERR_LocalIllegallyOverridesDiscardParameter = 8752, - ERR_CannotReferenceDiscards = 8753, - ERR_DiscardNamedArgument = 8754, // Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd) } From 0d86ac4ee50a10f2e1cb934f60df77da8a96a2f4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 28 Oct 2019 13:08:10 -0700 Subject: [PATCH 16/27] Only support lambdas --- .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../CSharp/Portable/Binder/Binder_Lookup.cs | 5 - .../Portable/Binder/Binder_NameConflicts.cs | 17 - .../CSharp/Portable/Binder/InMethodBinder.cs | 5 +- .../OverloadResolution_ArgsToParameters.cs | 2 +- .../Portable/Binder/WithParametersBinder.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 9 + .../CSharp/Portable/CSharpResources.resx | 4 +- .../Compiler/DocumentationCommentCompiler.cs | 2 +- .../CSharp/Portable/Errors/MessageID.cs | 4 +- .../Symbols/Source/ParameterHelpers.cs | 11 - .../Source/SourceClonedParameterSymbol.cs | 2 - .../Source/SourceComplexParameterSymbol.cs | 25 +- .../Symbols/Source/SourceParameterSymbol.cs | 14 +- .../Source/SourceSimpleParameterSymbol.cs | 22 +- ...SynthesizedAccessorValueParameterSymbol.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 +- .../Portable/xlf/CSharpResources.de.xlf | 10 +- .../Portable/xlf/CSharpResources.es.xlf | 10 +- .../Portable/xlf/CSharpResources.fr.xlf | 10 +- .../Portable/xlf/CSharpResources.it.xlf | 10 +- .../Portable/xlf/CSharpResources.ja.xlf | 10 +- .../Portable/xlf/CSharpResources.ko.xlf | 10 +- .../Portable/xlf/CSharpResources.pl.xlf | 10 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 +- .../Portable/xlf/CSharpResources.ru.xlf | 10 +- .../Portable/xlf/CSharpResources.tr.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 +- .../Semantics/LambdaDiscardParametersTests.cs | 574 +----------------- 30 files changed, 126 insertions(+), 706 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index db62c43f367b9..8b2c079762db6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -278,7 +278,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB if (seenDiscard) { // We only report the diagnostic on the second and subsequent underscores - MessageID.IDS_FeatureDiscardParameters.CheckFeatureAvailability( + MessageID.IDS_FeatureLambdaDiscardParameters.CheckFeatureAvailability( diagnostics, binder.Compilation, lambda.ParameterLocation(i)); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 441f44174b228..3b782c3ba40f2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1388,11 +1388,6 @@ internal bool CanAddLookupSymbolInfo(Symbol symbol, LookupOptions options, Looku Debug.Assert(options.AreValid()); HashSet useSiteDiagnostics = null; - if (symbol is ParameterSymbol { IsDiscard: true }) - { - return false; - } - var name = aliasSymbol != null ? aliasSymbol.Name : symbol.Name; if (!info.CanBeAdded(name)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs index 3300c5508f873..5e3a75c5a2cab 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs @@ -58,8 +58,6 @@ internal void ValidateParameterNameConflicts( if (!parameters.IsDefaultOrEmpty) { pNames = PooledHashSet.GetInstance(); - bool seenDiscard = false; - foreach (var p in parameters) { var name = p.Name; @@ -74,21 +72,6 @@ internal void ValidateParameterNameConflicts( diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name); } - if (p.IsDiscard) - { - if (seenDiscard) - { - // We only report the diagnostic on the second and subsequent underscores - MessageID.IDS_FeatureDiscardParameters.CheckFeatureAvailability( - diagnostics, - this.Compilation, - p.Locations[0]); - } - - seenDiscard = true; - continue; - } - if (!pNames.Add(name)) { // The parameter name '{0}' is a duplicate diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs index 1010333026d0f..4ca2ddcddeece 100644 --- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs @@ -229,10 +229,7 @@ internal override void LookupSymbolsInSingleBinder( parameterMap = new MultiDictionary(parameters.Length, EqualityComparer.Default); foreach (var parameter in parameters) { - if (!parameter.IsDiscard) - { - parameterMap.Add(parameter.Name, parameter); - } + parameterMap.Add(parameter.Name, parameter); } _lazyParameterMap = parameterMap; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs index bc49ea8dc9834..e05de2b51fa0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs @@ -313,7 +313,7 @@ private static ArgumentAnalysisResult AnalyzeArguments( { // p is initialized to zero; it is ok for a named argument to "correspond" to // _any_ parameter (not just the parameters past the point of positional arguments) - if (memberParameters[p].Name == name.Identifier.ValueText && !memberParameters[p].IsDiscard) + if (memberParameters[p].Name == name.Identifier.ValueText) { if (isValidParams && p == memberParameters.Length - 1) { diff --git a/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs index d0422a0d4ff75..be1cbcf60a47b 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithParametersBinder.cs @@ -49,7 +49,7 @@ internal override void LookupSymbolsInSingleBinder( foreach (ParameterSymbol parameter in _parameters) { - if (parameter.Name == name && !parameter.IsDiscard) + if (parameter.Name == name) { result.MergeEqual(originalBinder.CheckViability(parameter, arity, options, null, diagnose, ref useSiteDiagnostics)); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 800ff3a621dda..ce680305d3e29 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -11455,6 +11455,15 @@ internal static string IDS_FeatureLambda { } } + /// + /// Looks up a localized string similar to lambda discard parameters. + /// + internal static string IDS_FeatureLambdaDiscardParameters { + get { + return ResourceManager.GetString("IDS_FeatureLambdaDiscardParameters", resourceCulture); + } + } + /// /// Looks up a localized string similar to leading digit separator. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5e0dbebe93b6c..87d4118af63eb 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5744,8 +5744,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ name shadowing in nested functions - - discard parameters + + lambda discard parameters Cannot use a collection of dynamic type in an asynchronous foreach diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index 70a7a1640e813..620ea5f32528f 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -342,7 +342,7 @@ public override void DefaultVisit(Symbol symbol) { foreach (ParameterSymbol parameter in GetParameters(symbol)) { - if (!parameter.IsDiscard && !documentedParameters.Contains(parameter)) + if (!documentedParameters.Contains(parameter)) { Location location = parameter.Locations[0]; Debug.Assert(location.SourceTree.ReportDocumentationCommentDiagnostics()); //Should be the same tree as for the symbol. diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index d0abd8b6466cc..890829783d39c 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -182,7 +182,7 @@ internal enum MessageID IDS_FeatureNestedStackalloc = MessageBase + 12762, IDS_FeatureSwitchExpression = MessageBase + 12763, IDS_FeatureAsyncUsing = MessageBase + 12764, - IDS_FeatureDiscardParameters = MessageBase + 12765, + IDS_FeatureLambdaDiscardParameters = MessageBase + 12765, } // Message IDs may refer to strings that need to be localized. @@ -291,7 +291,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) switch (feature) { // Preview features. - case MessageID.IDS_FeatureDiscardParameters: + case MessageID.IDS_FeatureLambdaDiscardParameters: return LanguageVersion.Preview; // C# 8.0 features. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index a21e270e5dc6e..2e546b3746b4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -29,15 +29,6 @@ public static ImmutableArray MakeParameters( var builder = ArrayBuilder.GetInstance(); var mustBeLastParameter = (ParameterSyntax)null; - int discardsCount = 0; - foreach (var parameterSyntax in syntax.Parameters) - { - if (parameterSyntax.Identifier.IsUnderscoreToken()) - { - discardsCount++; - } - } - foreach (var parameterSyntax in syntax.Parameters) { if (mustBeLastParameter == null) @@ -88,7 +79,6 @@ public static ImmutableArray MakeParameters( diagnostics.Add(ErrorCode.ERR_IllegalRefParam, refnessKeyword.GetLocation()); } - bool isDiscard = discardsCount >= 2 && parameterSyntax.Identifier.IsUnderscoreToken(); var parameter = SourceParameterSymbol.Create( binder, owner, @@ -100,7 +90,6 @@ public static ImmutableArray MakeParameters( (paramsKeyword.Kind() != SyntaxKind.None), parameterIndex == 0 && thisKeyword.Kind() != SyntaxKind.None, addRefReadOnlyModifier, - isDiscard, diagnostics); ReportParameterErrors(owner, parameterSyntax, parameter, thisKeyword, paramsKeyword, firstDefault, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs index 1ef6d0dfbae8a..ab9db68a26ef5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs @@ -31,8 +31,6 @@ internal SourceClonedParameterSymbol(SourceParameterSymbol originalParam, Symbol public override bool IsDiscard => _originalParam.IsDiscard; - public override string MetadataName => _originalParam.MetadataName; - public override ImmutableArray DeclaringSyntaxReferences { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index a9fb3f9874c2e..ccdfa26fbec68 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -38,13 +38,12 @@ internal SourceComplexParameterSymbol( TypeWithAnnotations parameterType, RefKind refKind, string name, - bool isDiscard, ImmutableArray locations, SyntaxReference syntaxRef, ConstantValue defaultSyntaxValue, bool isParams, bool isExtensionMethodThis) - : base(owner, parameterType, ordinal, refKind, name, isDiscard, locations) + : base(owner, parameterType, ordinal, refKind, name, locations) { Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter))); @@ -78,6 +77,8 @@ internal SourceComplexParameterSymbol( internal SyntaxTree SyntaxTree => _syntaxRef == null ? null : _syntaxRef.SyntaxTree; + public sealed override bool IsDiscard => false; + internal override ConstantValue ExplicitDefaultConstantValue { get @@ -316,27 +317,18 @@ public override string MetadataName // The metadata parameter name should be the name used in the partial definition. var sourceMethod = this.ContainingSymbol as SourceOrdinaryMethodSymbol; - if (sourceMethod is null) + if ((object)sourceMethod == null) { - return baseMetadataNameOrDiscard(); + return base.MetadataName; } var definition = sourceMethod.SourcePartialDefinition; - if (definition is null) + if ((object)definition == null) { - return baseMetadataNameOrDiscard(); + return base.MetadataName; } return definition.Parameters[this.Ordinal].MetadataName; - - string baseMetadataNameOrDiscard() - { - if (IsDiscard) - { - return DiscardMetadataName(Ordinal); - } - return base.MetadataName; - } } } @@ -1157,13 +1149,12 @@ internal SourceComplexParameterSymbolWithCustomModifiersPrecedingByRef( RefKind refKind, ImmutableArray refCustomModifiers, string name, - bool isDiscard, ImmutableArray locations, SyntaxReference syntaxRef, ConstantValue defaultSyntaxValue, bool isParams, bool isExtensionMethodThis) - : base(owner, ordinal, parameterType, refKind, name, isDiscard, locations, syntaxRef, defaultSyntaxValue, isParams, isExtensionMethodThis) + : base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, defaultSyntaxValue, isParams, isExtensionMethodThis) { Debug.Assert(!refCustomModifiers.IsEmpty); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 02c5b9fffb92f..2ebe74b3a9a63 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -19,7 +19,6 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase protected SymbolCompletionState state; protected readonly TypeWithAnnotations parameterType; private readonly string _name; - private readonly bool _isDiscard; private readonly ImmutableArray _locations; private readonly RefKind _refKind; @@ -34,7 +33,6 @@ public static SourceParameterSymbol Create( bool isParams, bool isExtensionMethodThis, bool addRefReadOnlyModifier, - bool isDiscard, DiagnosticBag declarationDiagnostics) { var name = identifier.ValueText; @@ -60,7 +58,6 @@ public static SourceParameterSymbol Create( refKind, ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)), name, - isDiscard, locations, syntax.GetReference(), ConstantValue.Unset, @@ -74,7 +71,7 @@ public static SourceParameterSymbol Create( (syntax.AttributeLists.Count == 0) && !owner.IsPartialMethod()) { - return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, isDiscard, locations); + return new SourceSimpleParameterSymbol(owner, parameterType, ordinal, refKind, name, isDiscard: false, locations); } return new SourceComplexParameterSymbol( @@ -83,7 +80,6 @@ public static SourceParameterSymbol Create( parameterType, refKind, name, - isDiscard, locations, syntax.GetReference(), ConstantValue.Unset, @@ -97,7 +93,6 @@ protected SourceParameterSymbol( int ordinal, RefKind refKind, string name, - bool isDiscard, ImmutableArray locations) : base(owner, ordinal) { @@ -111,7 +106,6 @@ protected SourceParameterSymbol( this.parameterType = parameterType; _refKind = refKind; _name = name; - _isDiscard = isDiscard; _locations = locations; } @@ -134,7 +128,6 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy newTypeWithModifiers, _refKind, _name, - _isDiscard, _locations, this.SyntaxReference, this.ExplicitDefaultConstantValue, @@ -152,7 +145,6 @@ internal SourceParameterSymbol WithCustomModifiersAndParamsCore(TypeSymbol newTy _refKind, newRefCustomModifiers, _name, - _isDiscard, _locations, this.SyntaxReference, this.ExplicitDefaultConstantValue, @@ -211,10 +203,6 @@ internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics) internal abstract bool IsExtensionMethodThis { get; } - public sealed override bool IsDiscard => _isDiscard; - - protected static string DiscardMetadataName(int ordinal) => $"<>_{ordinal + 1}"; - public sealed override RefKind RefKind { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index 096b80fc9944c..028ff67f0e6e2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -12,21 +12,19 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal sealed class SourceSimpleParameterSymbol : SourceParameterSymbol { public SourceSimpleParameterSymbol( - Symbol owner, - TypeWithAnnotations parameterType, - int ordinal, - RefKind refKind, - string name, - bool isDiscard, - ImmutableArray locations) - : base(owner, parameterType, ordinal, refKind, name, isDiscard, locations) + Symbol owner, + TypeWithAnnotations parameterType, + int ordinal, + RefKind refKind, + string name, + bool isDiscard, + ImmutableArray locations) + : base(owner, parameterType, ordinal, refKind, name, locations) { + IsDiscard = isDiscard; } - public override string MetadataName - { - get { return IsDiscard ? DiscardMetadataName(Ordinal) : base.MetadataName; } - } + public override bool IsDiscard { get; } internal override ConstantValue ExplicitDefaultConstantValue { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs index 786cb715b99df..3110d6ec34148 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedAccessorValueParameterSymbol.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal sealed class SynthesizedAccessorValueParameterSymbol : SourceComplexParameterSymbol { public SynthesizedAccessorValueParameterSymbol(SourceMemberMethodSymbol accessor, TypeWithAnnotations paramType, int ordinal) - : base(accessor, ordinal, paramType, RefKind.None, ParameterSymbol.ValueParameterName, isDiscard: false, accessor.Locations, + : base(accessor, ordinal, paramType, RefKind.None, ParameterSymbol.ValueParameterName, accessor.Locations, syntaxRef: null, defaultSyntaxValue: ConstantValue.Unset, // the default value can be set via [param: DefaultParameterValue] applied on the accessor isParams: false, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index e7117a134f60f..c6c1f8380ccee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -814,11 +814,6 @@ delegovat obecná omezení typu - - discard parameters - discard parameters - - enum generic type constraints výčet obecných omezení typu @@ -844,6 +839,11 @@ indexování mobilních vyrovnávacích pamětí pevné velikosti + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions skrývání názvů ve vnořených funkcích diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index d8909816fac34..05cf96985ae8b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -814,11 +814,6 @@ Generische Typeneinschränkungen für Delegat - - discard parameters - discard parameters - - enum generic type constraints Generische Typeneinschränkungen für Enumeration @@ -844,6 +839,11 @@ Bewegliche Puffer fester Größe werden indiziert. + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions Namensshadowing in geschachtelten Funktionen diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 35b19b8577bf2..16f12b519f989 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -815,11 +815,6 @@ restricciones de tipo genérico delegate - - discard parameters - discard parameters - - enum generic type constraints restricciones de tipo genérico enum @@ -845,6 +840,11 @@ indexando búferes fijos movibles + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions sombreado de nombres en funciones anidadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index ed5afe11e02b8..4633e814b6ad7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -814,11 +814,6 @@ contraintes de type générique de délégué - - discard parameters - discard parameters - - enum generic type constraints contraintes de type générique d'enum @@ -844,6 +839,11 @@ indexation de mémoires tampons fixes mobiles + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions ombrage des noms dans les fonctions imbriquées diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 3b816273193f5..a416d3ef870bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -814,11 +814,6 @@ vincoli di tipo generico delegato - - discard parameters - discard parameters - - enum generic type constraints vincoli di tipo generico enumerazione @@ -844,6 +839,11 @@ indicizzazione di buffer fissi mobili + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions shadowing dei nomi nelle funzioni annidate diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 600531557050d..fb268e7ea6abb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -814,11 +814,6 @@ delegate ジェネリック型の制約 - - discard parameters - discard parameters - - enum generic type constraints enum ジェネリック型の制約 @@ -844,6 +839,11 @@ 移動可能な固定バッファーのインデックス化 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 入れ子になった関数での名前シャドウイング diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 7b9a766d03650..545f933b84321 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -814,11 +814,6 @@ 대리자 제네릭 형식 제약 조건 - - discard parameters - discard parameters - - enum generic type constraints 열거형 제네릭 형식 제약 조건 @@ -844,6 +839,11 @@ 이동 가능한 고정 버퍼 인덱싱 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 중첩된 함수의 이름 섀도잉 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index cd892b2865e26..4f448cf06c787 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -814,11 +814,6 @@ ogólne ograniczenia typów delegowania - - discard parameters - discard parameters - - enum generic type constraints ogólne ograniczenia typów wyliczenia @@ -844,6 +839,11 @@ indeksowanie możliwych do przenoszenia buforów fixed + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions zasłanianie nazw w funkcjach zagnieżdżonych diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 7c22ff829cd53..34c22baa89db9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -814,11 +814,6 @@ restrições de tipo genérico delegate - - discard parameters - discard parameters - - enum generic type constraints restrições de tipo genérico enum @@ -844,6 +839,11 @@ buffers fixos móveis de indexação + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions sombreamento de nome em funções aninhadas diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index d14505c910509..5528e5c46fb66 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -814,11 +814,6 @@ ограничения универсального типа для делегата - - discard parameters - discard parameters - - enum generic type constraints ограничения универсального типа перечисления @@ -844,6 +839,11 @@ индексирование перемещаемых буферов фиксированного размера + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions скрытие имен во вложенных функциях diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index bd5fcbd50da4c..a499f7d76c762 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -814,11 +814,6 @@ delegate genel tür kısıtlamaları - - discard parameters - discard parameters - - enum generic type constraints enum genel tür kısıtlamaları @@ -844,6 +839,11 @@ taşınabilir sabit arabellekler dizine alınıyor + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions iç içe işlevlerde ad gölgeleme diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 407379b591b14..cd58137933b6c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -814,11 +814,6 @@ 委托泛型类型约束 - - discard parameters - discard parameters - - enum generic type constraints 枚举泛型类型约束 @@ -844,6 +839,11 @@ 正在编制可移动固定缓冲区的索引 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 在嵌套函数中的名称映射 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ccf9afbd8d6ca..2c04cbcb911fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -814,11 +814,6 @@ 委派泛型類型條件約束 - - discard parameters - discard parameters - - enum generic type constraints 列舉泛型類型條件約束 @@ -844,6 +839,11 @@ 對可移動的固定緩衝區編製索引 + + lambda discard parameters + lambda discard parameters + + name shadowing in nested functions 巢狀函式中的名稱鏡像處理 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 4f3a6e34e1e1c..1c19ab5c0cf76 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -53,24 +53,24 @@ public static void Main() }", parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( - // (6,51): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (6,51): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // System.Func f1 = (_, _) => 3L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(6, 51), - // (10,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(6, 51), + // (10,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 4L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(10, 13), - // (13,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(10, 13), + // (13,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 5L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(13, 13), - // (16,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(13, 13), + // (16,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _, - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(16, 13), - // (17,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(16, 13), + // (17,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _) => 6L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(17, 13), - // (20,13): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(17, 13), + // (20,13): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // _, - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(20, 13) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(20, 13) ); var tree = comp.SyntaxTrees.Single(); @@ -81,7 +81,7 @@ public static void Main() } [Fact] - public void DiscardParameters_CSharp8_LocalFunctions() + public void DiscardParameters_LocalFunctions() { var comp = CreateCompilation(@" public class C @@ -91,41 +91,29 @@ public static void Main() long f1(short _, string _) => 3L; System.Console.WriteLine(f1(1, null)); } -}", parseOptions: TestOptions.Regular8); +}", parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (6,33): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (6,33): error CS0100: The parameter name '_' is a duplicate // long f1(short _, string _) => 3L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(6, 33) + Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(6, 33) ); - - var tree = comp.SyntaxTrees.Single(); - var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.Identifier.ToString() == "_").ToArray(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); - VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); } [Fact] - public void DiscardParameters_CSharp8_Methods() + public void DiscardParameters_Methods() { var comp = CreateCompilation(@" public class C { public long M(short _, string _) => 3L; -}", parseOptions: TestOptions.Regular8); +}", parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (4,35): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (4,35): error CS0100: The parameter name '_' is a duplicate // public long M(short _, string _) => 3L; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(4, 35) + Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(4, 35) ); - - var tree = comp.SyntaxTrees.Single(); - var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.Identifier.ToString() == "_").ToArray(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - VerifyDiscardParameterSymbol(underscores[0], "System.Int16", CodeAnalysis.NullableAnnotation.NotAnnotated, model); - VerifyDiscardParameterSymbol(underscores[1], "System.String", CodeAnalysis.NullableAnnotation.None, model); } private static void VerifyDiscardParameterSymbol(ParameterSyntax underscore, string expectedType, CodeAnalysis.NullableAnnotation expectedAnnotation, SemanticModel model) @@ -199,526 +187,10 @@ void local(int _, int _) { } } }"); - comp.VerifyDiagnostics(); - } - - [Fact] - public void DiscardParameters_OnLocalFunction_NotInScope() - { - var comp = CreateCompilation(@" -class C -{ - static void M() - { - int _ = 0; - local(1, 2); - void local(int _, int _) { _++; } - } -}"); - - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var underscore = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").Single(); - - var localSymbol = model.GetSymbolInfo(underscore).Symbol; - Assert.Equal("System.Int32 _", localSymbol.ToTestDisplayString()); - Assert.Equal(SymbolKind.Local, localSymbol.Kind); - } - - [Fact] - public void DiscardParameters_OnMethod() - { - var comp = CreateCompilation(@" -public class C -{ - public static void M(int _, int _) - { - M(1, 2); - _ = """"; - } -}"); - - comp.VerifyDiagnostics(); - - var comp2 = CreateCompilation(@" -class D -{ - public static void M2() - { - C.M(1, 2); - } -} -", references: new[] { comp.EmitToImageReference() }); - comp2.VerifyDiagnostics(); - var method = comp2.GlobalNamespace.GetMember("C.M"); - Assert.Equal("void C.M(System.Int32 <>_1, System.Int32 <>_2)", method.ToTestDisplayString()); - - var comp3 = CreateCompilation(@" -class D -{ - public static void M2() - { - C.M(1, _: 2); - C.M(_: 1, 2); - } -} -", references: new[] { comp.EmitToImageReference() }); - comp3.VerifyDiagnostics( - // (6,16): error CS1739: The best overload for 'M' does not have a parameter named '_' - // C.M(1, _: 2); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 16), - // (7,13): error CS1739: The best overload for 'M' does not have a parameter named '_' - // C.M(_: 1, 2); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 13) - ); - } - - [Fact] - public void DiscardParameters_OnMethod_Partial() - { - var comp = CreateCompilation(@" -public class C -{ - public static void M(int _, int _) - { - M(1, 2); - _ = """"; - } -}"); - - comp.VerifyDiagnostics(); - - var comp2 = CreateCompilation(@" -class D -{ - public static void M2() - { - C.M(1, 2); - } -} -", references: new[] { comp.EmitToImageReference() }); - comp2.VerifyDiagnostics(); - - var comp3 = CreateCompilation(@" -class D -{ - public static void M2() - { - C.M(1, _: 2); - C.M(_: 1, 2); - } -} -", references: new[] { comp.EmitToImageReference() }); - comp3.VerifyDiagnostics( - // (6,16): error CS1739: The best overload for 'M' does not have a parameter named '_' - // C.M(1, _: 2); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 16), - // (7,13): error CS1739: The best overload for 'M' does not have a parameter named '_' - // C.M(_: 1, 2); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 13) - ); - } - - [Fact] - public void DiscardParameters_OnMethod_NamedArgument() - { - var comp = CreateCompilation(@" -class C -{ - static void M(int _, string _) - { - M(1, _: null); - M(_: 1, null); - } -}"); - - comp.VerifyDiagnostics( - // (6,14): error CS1739: The best overload for 'M' does not have a parameter named '_' - // M(1, _: null); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(6, 14), - // (7,11): error CS1739: The best overload for 'M' does not have a parameter named '_' - // M(_: 1, null); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M", "_").WithLocation(7, 11) - ); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var calls = tree.GetRoot().DescendantNodes().OfType().ToArray(); - Assert.Null(model.GetSymbolInfo(calls[0]).Symbol); - Assert.Null(model.GetSymbolInfo(calls[1]).Symbol); - } - - [Fact] - public void DiscardParameters_OnMethod_NamedArgument_Underscore() - { - var comp = CreateCompilation(@" -class C -{ - static void M(int a, string _) - { - M(1, _: null); - } -}"); - - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var call = tree.GetRoot().DescendantNodes().OfType().Single(); - Assert.Equal("void C.M(System.Int32 a, System.String _)", model.GetSymbolInfo(call).Symbol.ToTestDisplayString()); - } - - [Fact] - public void DiscardParameters_OnMethod_NamedArgument_Underscore2() - { - var comp = CreateCompilation(@" -class C -{ - void M(int a, string _) { } - void M(long _, string _) - { - M(1, _: null); - } -}"); - - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var call = tree.GetRoot().DescendantNodes().OfType().Single(); - Assert.Equal("void C.M(System.Int32 a, System.String _)", model.GetSymbolInfo(call).Symbol.ToTestDisplayString()); - } - - [Fact] - public void DiscardParameters_OnMethod_NamedArgumentDoesNotMatchDiscard() - { - var comp = CreateCompilation(@" -class C -{ - static void M(int _, string _) - { - M(1, b: null); - } -}"); - comp.VerifyDiagnostics( - // (6,14): error CS1739: The best overload for 'M' does not have a parameter named 'b' - // M(1, b: null); - Diagnostic(ErrorCode.ERR_BadNamedArgument, "b").WithArguments("M", "b").WithLocation(6, 14) - ); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var call = tree.GetRoot().DescendantNodes().OfType().Single(); - Assert.Null(model.GetSymbolInfo(call).Symbol); - } - - [Fact] - public void DiscardParameters_OnMethod_WithXmlDoc() - { - var comp = CreateCompilation(@" -class C -{ - /// - /// 1 - /// 2 - void M(int _, int _) - { - } -}", parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); - - comp.VerifyDiagnostics( - // (5,22): warning CS1572: XML comment has a param tag for '_', but there is no parameter by that name - // /// 1 - Diagnostic(ErrorCode.WRN_UnmatchedParamTag, "_").WithArguments("_").WithLocation(5, 22), - // (6,22): warning CS1572: XML comment has a param tag for '_', but there is no parameter by that name - // /// 2 - Diagnostic(ErrorCode.WRN_UnmatchedParamTag, "_").WithArguments("_").WithLocation(6, 22) - ); - } - - // TODO2 test as range variables? - - [Fact] - public void DiscardParameters_OnMethod_Overridding() - { - var comp = CreateCompilation(@" -public class Base -{ - public virtual void M(int _, int _) - { - } -} -public class C : Base -{ - public override void M(int _, int _) - { - } -}"); - - comp.VerifyDiagnostics(); - } - - [Fact] - public void DiscardParameters_OnMethod_Overridding_SettingNames() - { - var comp = CreateCompilation(@" -public class Base -{ - public virtual void M(int _, int _) - { - } -} -public class C : Base -{ - public override void M(int a, int b) - { - } -}"); - - comp.VerifyDiagnostics(); - } - - [Fact] - public void DiscardParameters_OnMethod_Overridding_RemovingNames() - { - var comp = CreateCompilation(@" -public class Base -{ - public virtual void M(int a, int b) - { - } -} -public class C : Base -{ - public override void M(int _, int _) - { - } -}"); - - comp.VerifyDiagnostics(); - } - - [Fact] - public void DiscardParameters_OnConstructor() - { - var comp = CreateCompilation(@" -class C -{ - C(int _, string _) - { - new C(1, null); - new C(1, _: null); // 1 - new C(_: 1, null); // 2 - _.ToString(); // 3 - } -}"); - - comp.VerifyDiagnostics( - // (7,18): error CS1739: The best overload for 'C' does not have a parameter named '_' - // new C(1, _: null); // 1 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("C", "_").WithLocation(7, 18), - // (8,15): error CS1739: The best overload for 'C' does not have a parameter named '_' - // new C(_: 1, null); // 2 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("C", "_").WithLocation(8, 15), - // (9,9): error CS0103: The name '_' does not exist in the current context - // _.ToString(); // 3 - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(9, 9) - ); - - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); - var calls = tree.GetRoot().DescendantNodes().OfType().ToArray(); - Assert.Equal("C..ctor(System.Int32 _, System.String _)", model.GetSymbolInfo(calls[0]).Symbol.ToTestDisplayString()); - Assert.Null(model.GetSymbolInfo(calls[1]).Symbol); - Assert.Null(model.GetSymbolInfo(calls[2]).Symbol); - } - - [Fact] - public void DiscardParameters_OnDelegate() - { - var comp = CreateCompilation(@" -class C -{ - delegate void Signature(int _, int _); - - static void M(Signature s) - { - s(1, _: 2); - } -}"); - - comp.VerifyDiagnostics( - // (8,14): error CS1746: The delegate 'C.Signature' does not have a parameter named '_' - // s(1, _: 2); - Diagnostic(ErrorCode.ERR_BadNamedArgumentForDelegateInvoke, "_").WithArguments("C.Signature", "_").WithLocation(8, 14) - ); - } - - [Fact] - public void DiscardParameters_VerifyMetadata() - { - var comp = CreateCompilation(@" -public class C -{ - public delegate int Delegate(string _, string _); - public int this[string _, string _] => throw null; - public int M1(string _, string _) => throw null; - public int M2(int a, string _, string _) => throw null; - public int M3(string _, int b, string _) => throw null; - public int M4(string _, string _, int c) => throw null; - public int M5(int a, string _, string _ = null) => throw null; -} - -public interface I -{ - int M(int _, string b, int _); -} -"); - comp.VerifyDiagnostics(); - - var comp2 = CreateCompilation("", new[] { comp.EmitToImageReference() }); - var cMembers = comp2.GetTypeByMetadataName("C").GetMembers(); - AssertEx.Equal(new[] { - "System.Int32 C.this[System.String <>_1, System.String <>_2].get", - "System.Int32 C.M1(System.String <>_1, System.String <>_2)", - "System.Int32 C.M2(System.Int32 a, System.String <>_2, System.String <>_3)", - "System.Int32 C.M3(System.String <>_1, System.Int32 b, System.String <>_3)", - "System.Int32 C.M4(System.String <>_1, System.String <>_2, System.Int32 c)", - "System.Int32 C.M5(System.Int32 a, System.String <>_2, [System.String <>_3 = null])", - "C..ctor()", - "System.Int32 C.this[System.String <>_1, System.String <>_2] { get; }", - "C.Delegate" }, - cMembers.Select(m => m.ToTestDisplayString())); - - var iMembers = comp2.GetTypeByMetadataName("I").GetMembers(); - AssertEx.Equal(new[] { - "System.Int32 I.M(System.Int32 <>_1, System.String b, System.Int32 <>_3)" }, - iMembers.Select(m => m.ToTestDisplayString())); - - var delegateMembers = cMembers.OfType().Single().GetMembers(); - AssertEx.Equal(new[] { - "C.Delegate..ctor(System.Object @object, System.IntPtr method)", - "System.Int32 C.Delegate.Invoke(System.String <>_1, System.String <>_2)", - "System.IAsyncResult C.Delegate.BeginInvoke(System.String <>_1, System.String <>_2, System.AsyncCallback callback, System.Object @object)", - "System.Int32 C.Delegate.EndInvoke(System.IAsyncResult result)" }, - delegateMembers.Select(m => m.ToTestDisplayString())); - } - - [Fact] - public void DiscardParameters_VerifyMetadata_OnPartialMethod() - { - var comp = CreateCompilation(@" -public partial class C -{ - partial void M1(string _, string _); - partial void M2(string a, string b); - partial void M3(string _, string _); - partial void M4(string _, string _ = null); -} -public partial class C -{ - partial void M1(string _, string _) => throw null; - partial void M2(string _, string _) => throw null; - partial void M3(string a, string b) => throw null; - partial void M4(string _, string _) => throw null; - - void M() - { - M1(null, null); - - M2(null, null); - M2(a: null, null); - M2(null, b: null); - M2(_: null, null); // 1 - M2(null, _: null); // 2 - - M3(null, null); - M3(a: null, null); // 3 - M3(null, b: null); // 4 - M3(_: null, null); // 5 - M3(null, _: null); // 6 - } -} -"); - comp.VerifyDiagnostics( - // (23,12): error CS1739: The best overload for 'M2' does not have a parameter named '_' - // M2(_: null, null); // 1 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M2", "_").WithLocation(23, 12), - // (24,18): error CS1739: The best overload for 'M2' does not have a parameter named '_' - // M2(null, _: null); // 2 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M2", "_").WithLocation(24, 18), - // (27,12): error CS1739: The best overload for 'M3' does not have a parameter named 'a' - // M3(a: null, null); // 3 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "a").WithArguments("M3", "a").WithLocation(27, 12), - // (28,18): error CS1739: The best overload for 'M3' does not have a parameter named 'b' - // M3(null, b: null); // 4 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "b").WithArguments("M3", "b").WithLocation(28, 18), - // (29,12): error CS1739: The best overload for 'M3' does not have a parameter named '_' - // M3(_: null, null); // 5 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M3", "_").WithLocation(29, 12), - // (30,18): error CS1739: The best overload for 'M3' does not have a parameter named '_' - // M3(null, _: null); // 6 - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("M3", "_").WithLocation(30, 18) - ); - } - - [Fact] - public void DiscardParameters_OnIndexer() - { - var comp = CreateCompilation(@" -class C1 -{ - int this[int _, int _] => _++; // 1 -}"); - - comp.VerifyDiagnostics( - // (4,31): error CS0103: The name '_' does not exist in the current context - // int this[int _, int _] => _++; // 1 - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(4, 31) - ); - - comp = CreateCompilation(@" -public class C -{ - public int this[int _, int _] => 1; -}"); - - comp.VerifyDiagnostics(); - - var comp2 = CreateCompilation(@" -class D -{ - public static void M2(C c) - { - _ = c[1, 2]; - } -} -", references: new[] { comp.EmitToImageReference() }); - comp2.VerifyDiagnostics(); - - var getter = comp2.GetTypeByMetadataName("C").GetMembers().OfType().Where(m => m.Name == "get_Item").Single(); - Assert.Equal("System.Int32 C.this[System.Int32 <>_1, System.Int32 <>_2].get", getter.ToTestDisplayString()); - - var comp3 = CreateCompilation(@" -class D -{ - public static void M2(C c) - { - _ = c[1, _: 2]; - _ = c[_: 1, 2]; - } -} -", references: new[] { comp.EmitToImageReference() }); - comp3.VerifyDiagnostics( - // (6,18): error CS1739: The best overload for 'this' does not have a parameter named '_' - // _ = c[1, _: 2]; - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("this", "_").WithLocation(6, 18), - // (7,15): error CS1739: The best overload for 'this' does not have a parameter named '_' - // _ = c[_: 1, 2]; - Diagnostic(ErrorCode.ERR_BadNamedArgument, "_").WithArguments("this", "_").WithLocation(7, 15) + // (7,31): error CS0100: The parameter name '_' is a duplicate + // void local(int _, int _) { } + Diagnostic(ErrorCode.ERR_DuplicateParamName, "_").WithArguments("_").WithLocation(7, 31) ); } From f53b947f9cb94a810a91a823a186c5a48865c557 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 28 Oct 2019 17:58:26 -0700 Subject: [PATCH 17/27] Fix tests --- .../Test/Semantic/Semantics/LambdaTests.cs | 6 ++--- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 27 ------------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 13000f5620a84..db585e6ea26eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -3339,9 +3339,9 @@ static void M() void verifyDiagnostics() { comp.VerifyDiagnostics( - // (8,37): error CS8652: The feature 'discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // Func f = (_, _) => 0; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("discard parameters").WithLocation(8, 37)); + // (8,37): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func f = (_, _) => 0; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(8, 37)); } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 0070d2f72499d..27cdc9b36c7ba 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -2691,33 +2691,6 @@ void M() MainDescription($"({FeaturesResources.discard}) int _")); } - [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] - public async Task TestMethodDiscardParameter_FirstDiscard() - { - await TestAsync( -@"class C -{ - int M(string $$_, int _) => 1; -}", - MainDescription($"({FeaturesResources.discard}) string _")); - } - - [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] - public async Task TestLocalFunctionDiscardParameter_SecondDiscard() - { - await TestAsync( -@"class C -{ - void M() - { - local(null, 0); - - int local(string _, int $$_) => 1; - } -}", - MainDescription($"({FeaturesResources.discard}) int _")); - } - [WorkItem(540871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540871")] [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestLiterals() From c2affd3e91074cbd22f53b6e23df813d9fb6cc20 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 30 Oct 2019 16:59:24 -0700 Subject: [PATCH 18/27] Address some PR feedback --- .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../Portable/Binder/WithLambdaParametersBinder.cs | 7 ++----- .../CSharp/Portable/BoundTree/UnboundLambda.cs | 4 ++-- src/Compilers/CSharp/Portable/Errors/MessageID.cs | 3 +-- .../Symbols/Source/SourceComplexParameterSymbol.cs | 1 + .../Portable/Symbols/Source/SourceParameterSymbol.cs | 2 ++ .../Semantics/LambdaDiscardParametersTests.cs | 12 +++++++----- .../VisualBasic/Portable/Symbols/ParameterSymbol.vb | 12 ++++++------ 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 8b2c079762db6..7f351b99477a2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -244,7 +244,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB var lambda = AnalyzeAnonymousFunction(syntax, diagnostics); var data = (PlainUnboundLambdaState)lambda.Data; - if (data.HasTypes) + if (data.HasExplicitlyTypedParameterList) { for (int i = 0; i < lambda.ParameterCount; i++) { diff --git a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs index b571ef026ca0a..4d2dcaf3dcd6b 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs @@ -37,7 +37,7 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) void recordDefinitions(ImmutableArray definitions) { - var declarationMap = _definitionMap ?? (_definitionMap = new SmallDictionary()); + var declarationMap = _definitionMap ??= new SmallDictionary(); foreach (var s in definitions) { if (!s.IsDiscard && !declarationMap.ContainsKey(s.Name)) @@ -95,10 +95,7 @@ internal override void LookupSymbolsInSingleBinder( foreach (var parameterSymbol in parameterMap[name]) { - if (!parameterSymbol.IsDiscard) - { - result.MergeEqual(originalBinder.CheckViability(parameterSymbol, arity, options, null, diagnose, ref useSiteDiagnostics)); - } + result.MergeEqual(originalBinder.CheckViability(parameterSymbol, arity, options, null, diagnose, ref useSiteDiagnostics)); } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 028c4064078b1..411a6936094f1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -1145,7 +1145,7 @@ public override Location ParameterLocation(int index) public override string ParameterName(int index) { Debug.Assert(!_parameterNames.IsDefault && 0 <= index && index < _parameterNames.Length); - return _parameterNames.IsDefault ? null : _parameterNames[index]; + return _parameterNames[index]; } public override bool ParameterIsDiscard(int index) @@ -1163,7 +1163,7 @@ public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) { Debug.Assert(this.HasExplicitlyTypedParameterList); Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length); - return _parameterTypesWithAnnotations.IsDefault ? default : _parameterTypesWithAnnotations[index]; + return _parameterTypesWithAnnotations[index]; } protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 890829783d39c..b153e0c26542b 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -281,7 +281,6 @@ private static CSDiagnosticInfo GetDisabledFeatureDiagnosticInfo(MessageID featu : new CSDiagnosticInfo(availableVersion.GetErrorCode(), feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion)); } - internal static LanguageVersion RequiredVersion(this MessageID feature) { Debug.Assert(RequiredFeature(feature) == null); @@ -291,7 +290,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) switch (feature) { // Preview features. - case MessageID.IDS_FeatureLambdaDiscardParameters: + case MessageID.IDS_FeatureLambdaDiscardParameters: // semantic check return LanguageVersion.Preview; // C# 8.0 features. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index ccdfa26fbec68..2162ec8878cd2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -46,6 +46,7 @@ internal SourceComplexParameterSymbol( : base(owner, parameterType, ordinal, refKind, name, locations) { Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter))); + Debug.Assert(!(owner is LambdaSymbol)); // therefore we're not dealing with discard parameters _lazyHasOptionalAttribute = ThreeState.Unknown; _syntaxRef = syntaxRef; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 2ebe74b3a9a63..d01d3683b55c0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -35,6 +35,8 @@ public static SourceParameterSymbol Create( bool addRefReadOnlyModifier, DiagnosticBag declarationDiagnostics) { + Debug.Assert(!(owner is LambdaSymbol)); // therefore we don't need to deal with discard parameters + var name = identifier.ValueText; var locations = ImmutableArray.Create(new SourceLocation(identifier)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 1c19ab5c0cf76..32142e3c7cec6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -166,7 +166,6 @@ static void M() } }"); - // Note: this is somewhat problematic because there is nothing the user can do to fix this. We could have an error for out discards comp.VerifyDiagnostics( // (9,17): error CS0177: The out parameter '_' must be assigned to before control leaves the current method // return 2; @@ -397,14 +396,17 @@ class C static void M() { System.Func f = (_, _) => { long _ = 0; return _++; }; - System.Func f2 = (_, a) => { long _ = 0; return _++; }; + System.Func f2 = (_, a) => { + long _ = 0; // 1 + return _++; + }; } }"); // Note that naming one of the parameters seems irrelevant but results in a binding change comp.VerifyDiagnostics( - // (7,65): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter - // System.Func f2 = (_, a) => { long _ = 0; return _++; }; - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(7, 65) + // (8,18): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // long _ = 0; // 1 + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(8, 18) ); var tree = comp.SyntaxTrees.Single(); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb index 60451133fccc8..6680c8e7c814c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ParameterSymbol.vb @@ -65,12 +65,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Friend ReadOnly Property IsDiscard As Boolean Implements IParameterSymbol.IsDiscard - Get - Return False - End Get - End Property - ''' ''' Describes how the parameter is marshalled when passed to native code. ''' Null if no specific marshalling information is available for the parameter. @@ -284,6 +278,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols #Region "IParameterSymbol" + Private ReadOnly Property IParameterSymbol_IsDiscard As Boolean Implements IParameterSymbol.IsDiscard + Get + Return False + End Get + End Property + Private ReadOnly Property IParameterSymbol_RefKind As RefKind Implements IParameterSymbol.RefKind Get ' TODO: Should we check if it has the attribute and return 'RefKind.Out' in From 0e71122d3c61e98e61dac87d050ceb86fe638f3c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 31 Oct 2019 10:41:31 -0700 Subject: [PATCH 19/27] Address remaining feedback --- .../Binder/Binder.QueryUnboundLambdaState.cs | 1 + .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../Portable/BoundTree/UnboundLambda.cs | 6 +-- .../Portable/Symbols/ParameterSymbol.cs | 2 + .../Semantics/LambdaDiscardParametersTests.cs | 52 ++++++++++++++----- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index cf0e1753f3095..21dd58566804a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -28,6 +28,7 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, public override string ParameterName(int index) { return _parameters[index].Name; } public override bool ParameterIsDiscard(int index) { return false; } + public override bool HasNames { get { return false; } } public override bool HasSignature { get { return true; } } public override bool HasExplicitlyTypedParameterList { get { return false; } } public override int ParameterCount { get { return _parameters.Length; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 7f351b99477a2..dc38c6d1a79c0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -243,7 +243,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB Debug.Assert(syntax.IsAnonymousFunction()); var lambda = AnalyzeAnonymousFunction(syntax, diagnostics); - var data = (PlainUnboundLambdaState)lambda.Data; + var data = lambda.Data; if (data.HasExplicitlyTypedParameterList) { for (int i = 0; i < lambda.ParameterCount; i++) diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 411a6936094f1..23bb303c112f0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -422,6 +422,8 @@ public void SetUnboundLambda(UnboundLambda unbound) public abstract bool HasExplicitlyTypedParameterList { get; } public abstract int ParameterCount { get; } public abstract bool IsAsync { get; } + public abstract bool HasNames { get; } + public abstract Location ParameterLocation(int index); public abstract TypeWithAnnotations ParameterTypeWithAnnotations(int index); public abstract RefKind RefKind(int index); @@ -1102,9 +1104,7 @@ internal PlainUnboundLambdaState( _isAsync = isAsync; } - internal bool HasNames { get { return !_parameterNames.IsDefault; } } - - internal bool HasTypes { get { return !_parameterTypesWithAnnotations.IsDefault; } } + public override bool HasNames { get { return !_parameterNames.IsDefault; } } public override bool HasSignature { get { return !_parameterNames.IsDefault; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index fbf40a51f5a5a..61f2a6c2ac459 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -59,6 +59,8 @@ protected override sealed Symbol OriginalSymbolDefinition /// public abstract RefKind RefKind { get; } + bool IParameterSymbol.IsDiscard => IsDiscard; + /// /// Returns true if the parameter is a discard parameter. /// diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 32142e3c7cec6..00c337369ec30 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -237,6 +237,28 @@ public static void Main() ); } + [Fact] + public void DiscardParameters_SingleUnderscoreParameter() + { + var comp = CreateCompilation(@" +public class C +{ + public static void Main() + { + System.Func f1 = (_, a) => + { + int _ = 0; // 1 + return _; + }; + } +}"); + comp.VerifyDiagnostics( + // (8,17): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // int _ = 0; // 1 + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(8, 17) + ); + } + [Fact] public void DiscardParameters_WithTypes() { @@ -334,22 +356,26 @@ public static void Main() public void DiscardParameters_NotInScope_BindToOutsideLocal() { var comp = CreateCompilation(@" -class C +public class C { - static void M() + public static void Main() { - int _ = 0; - System.Func f = (_, _) => _++; - System.Func f2 = (_, a) => _++; + int _ = 42; + System.Func f = (_, _) => ++_; + System.Func f2 = (_, a) => ++_; + System.Console.Write(f(null, null) + "" ""); + System.Console.Write(f2(1, null) + "" ""); + System.Console.Write(_); } -}"); +}", options: TestOptions.DebugExe); // Note that naming one of the parameters seems irrelevant but results in a binding change comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "43 2 43"); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); var underscores = tree.GetRoot().DescendantNodes().OfType().Where(p => p.ToString() == "_").ToArray(); - Assert.Equal(2, underscores.Length); + Assert.Equal(3, underscores.Length); var localSymbol = model.GetSymbolInfo(underscores[0]).Symbol; Assert.Equal("System.Int32 _", localSymbol.ToTestDisplayString()); @@ -364,19 +390,21 @@ static void M() public void DiscardParameters_NotInScope_BindToOutsideLocal_Nested() { var comp = CreateCompilation(@" -class C +public class C { - static void M() + public static void Main() { - int _ = 0; + int _ = 42; System.Func f = (_, _) => { - System.Func f2 = (_, _) => _++; + System.Func f2 = (_, _) => ++_; return f2(null, null); }; + System.Console.Write(f(null, null)); } -}"); +}", options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "43"); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); From 84d2ae2e2768a7de1dc01d1dc0e895a7ed8b8e0e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 1 Nov 2019 12:21:25 -0700 Subject: [PATCH 20/27] Address more feedback --- .../Binder/Binder.QueryUnboundLambdaState.cs | 2 +- .../Semantics/LambdaDiscardParametersTests.cs | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index 21dd58566804a..4b3fab3efd55b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -28,7 +28,7 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, public override string ParameterName(int index) { return _parameters[index].Name; } public override bool ParameterIsDiscard(int index) { return false; } - public override bool HasNames { get { return false; } } + public override bool HasNames { get { return true; } } public override bool HasSignature { get { return true; } } public override bool HasExplicitlyTypedParameterList { get { return false; } } public override int ParameterCount { get { return _parameters.Length; } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 00c337369ec30..4fbf162f2fcaf 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -259,6 +259,41 @@ public static void Main() ); } + [Fact] + public void DiscardParameters_SingleUnderscoreParameter_InScopeWithUnderscoreLocal() + { + var src = @" +public class C +{ + public static int M() + { + int _ = 0; + System.Func f1 = (_, a) => 0; + System.Func f2 = (_, _) => 0; + return _; + } +}"; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics( + // (7,47): error CS0136: A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // System.Func f1 = (_, a) => 0; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "_").WithArguments("_").WithLocation(7, 47), + // (8,50): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Func f2 = (_, _) => 0; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(8, 50) + ); + + var comp2 = CreateCompilation(src, parseOptions: TestOptions.Regular8); + comp2.VerifyDiagnostics( + // (8,50): error CS8652: The feature 'lambda discard parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Func f2 = (_, _) => 0; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "_").WithArguments("lambda discard parameters").WithLocation(8, 50) + ); + + var comp3 = CreateCompilation(src, parseOptions: TestOptions.RegularPreview); + comp3.VerifyDiagnostics(); + } + [Fact] public void DiscardParameters_WithTypes() { From 29e4414825062f9b2f1af4075580fae03262fd9c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 1 Nov 2019 15:36:12 -0700 Subject: [PATCH 21/27] Fix conflict --- src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs | 2 -- .../CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index fb26293db0c38..1059e58de0c47 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -60,8 +60,6 @@ protected override sealed Symbol OriginalSymbolDefinition /// public abstract RefKind RefKind { get; } - bool IParameterSymbol.IsDiscard => IsDiscard; - /// /// Returns true if the parameter is a discard parameter. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs index 82509f979d256..0f812bfec450e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/ParameterSymbol.cs @@ -54,6 +54,8 @@ IParameterSymbol IParameterSymbol.OriginalDefinition RefKind IParameterSymbol.RefKind => _underlying.RefKind; + bool IParameterSymbol.IsDiscard => _underlying.IsDiscard; + bool IParameterSymbol.IsParams => _underlying.IsParams; bool IParameterSymbol.IsOptional => _underlying.IsOptional; From 01b013c98050be191e9f66d6b3242c791ee73692 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 5 Nov 2019 15:37:02 -0800 Subject: [PATCH 22/27] Update PublicAPI.Unshipped.txt --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 02b9f51d34609..4f278d5608fb8 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -4,11 +4,6 @@ Microsoft.CodeAnalysis.ErrorLogOptions Microsoft.CodeAnalysis.ErrorLogOptions.ErrorLogOptions(string path, Microsoft.CodeAnalysis.SarifVersion sarifVersion) -> void Microsoft.CodeAnalysis.ErrorLogOptions.Path.get -> string Microsoft.CodeAnalysis.ErrorLogOptions.SarifVersion.get -> Microsoft.CodeAnalysis.SarifVersion -Microsoft.CodeAnalysis.Operations.VariableDeclarationKind -Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.AsynchronousUsing = 2 -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind -Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.Default = 0 -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind -Microsoft.CodeAnalysis.Operations.VariableDeclarationKind.Using = 1 -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind -Microsoft.CodeAnalysis.Operations.IVariableDeclarationGroupOperation.DeclarationKind.get -> Microsoft.CodeAnalysis.Operations.VariableDeclarationKind Microsoft.CodeAnalysis.IParameterSymbol.IsDiscard.get -> bool Microsoft.CodeAnalysis.OperationKind.UsingDeclaration = 108 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.Operations.IUsingDeclarationOperation @@ -35,4 +30,3 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitUsingDeclaration(Microsoft.CodeAnalysis.Operations.IUsingDeclarationOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.Operations.IPropertySubpatternOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation, TArgument argument) -> TResult -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitUsingDeclaration(Microsoft.CodeAnalysis.Operations.IUsingDeclarationOperation operation, TArgument argument) -> TResult From aa0dff872534d2dbfa208f18eab378428c85dcfa Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 5 Nov 2019 15:37:37 -0800 Subject: [PATCH 23/27] Update PublicAPI.Unshipped.txt --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 4f278d5608fb8..0dbc68621dde6 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -30,3 +30,4 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitUsingDeclaration(Microsoft.CodeAnalysis.Operations.IUsingDeclarationOperation operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitPropertySubpattern(Microsoft.CodeAnalysis.Operations.IPropertySubpatternOperation operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitRecursivePattern(Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitUsingDeclaration(Microsoft.CodeAnalysis.Operations.IUsingDeclarationOperation operation, TArgument argument) -> TResult From 9d910b8468fd8d0dc6cef2d4688e541207b0d72e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 12 Nov 2019 11:09:16 -0800 Subject: [PATCH 24/27] Add ChangeSignature tests --- .../ChangeSignature/ChangeSignatureTests.cs | 55 +++++++++++++++++++ .../ChangeSignatureViewModelTests.vb | 38 ------------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs index 9cd0f92f26e93..460d31383547a 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs @@ -64,6 +64,61 @@ await TestChangeSignatureViaCommandAsync( expectedUpdatedInvocationDocumentCode: expectedCode); } + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task TestOnLambdaWithTwoDiscardParameters_ViaCommand() + { + var markup = @" +class Program +{ + static void M() + { + System.Func f = $$(int _, string _) => 1; + } +}"; + var expectedCode = @" +class Program +{ + static void M() + { + System.Func f = (string _, int _) => 1; + } +}"; + + await TestChangeSignatureViaCommandAsync( + LanguageNames.CSharp, + markup: markup, + updatedSignature: new[] { 1, 0 }, + expectedUpdatedInvocationDocumentCode: expectedCode); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task TestOnAnonymousMethodWithTwoParameters_ViaCommand() + { + var markup = @" +class Program +{ + static void M() + { + System.Func f = $$delegate(int x, string y) { return 1; }; + } +}"; + await TestMissingAsync(markup); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task TestOnAnonymousMethodWithTwoDiscardParameters_ViaCommand() + { + var markup = @" +class Program +{ + static void M() + { + System.Func f = $$delegate(int _, string _) { return 1; }; + } +}"; + await TestMissingAsync(markup); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] public async Task TestAfterSemicolonForInvocationInExpressionStatement_ViaCodeAction() { diff --git a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb index 11080be2425c3..75460bd2d4b8e 100644 --- a/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb +++ b/src/VisualStudio/Core/Test/ChangeSignature/ChangeSignatureViewModelTests.vb @@ -114,44 +114,6 @@ class MyClass monitor.Detach() End Function - - Public Async Function ReorderParameters_MethodWithTwoDiscardParameters_MoveFirstParameterDown() As Tasks.Task - Dim markup = - - Dim viewModelTestState = Await GetViewModelTestStateAsync(markup, LanguageNames.CSharp) - Dim viewModel = viewModelTestState.ViewModel - VerifyOpeningState(viewModel, "public void M(int _, string _)") - - Dim monitor = New PropertyChangedTestMonitor(viewModel) - monitor.AddExpectation(Function() viewModel.IsOkButtonEnabled) - monitor.AddExpectation(Function() viewModel.SignatureDisplay) - monitor.AddExpectation(Function() viewModel.SignaturePreviewAutomationText) - monitor.AddExpectation(Function() viewModel.AllParameters) - monitor.AddExpectation(Function() viewModel.CanMoveUp) - monitor.AddExpectation(Function() viewModel.MoveUpAutomationText) - monitor.AddExpectation(Function() viewModel.CanMoveDown) - monitor.AddExpectation(Function() viewModel.MoveDownAutomationText) - - viewModel.MoveDown() - - VerifyAlteredState( - viewModelTestState, - monitor, - isOkButtonEnabled:=True, - canMoveUp:=True, - canMoveDown:=False, - permutation:={1, 0}, - signatureDisplay:="public void M(string _, int _)") - - monitor.Detach() - End Function - Public Async Function ReorderParameters_MethodWithTwoNormalParameters_RemoveFirstParameter() As Tasks.Task Dim markup = Date: Tue, 12 Nov 2019 11:27:34 -0800 Subject: [PATCH 25/27] Add SymbolCompletion tests --- .../ChangeSignature/ChangeSignatureTests.cs | 4 ++-- .../SymbolCompletionProviderTests.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs index 460d31383547a..fbf17d5f22dc4 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs @@ -99,7 +99,7 @@ class Program { static void M() { - System.Func f = $$delegate(int x, string y) { return 1; }; + System.Func f = [||]delegate(int x, string y) { return 1; }; } }"; await TestMissingAsync(markup); @@ -113,7 +113,7 @@ class Program { static void M() { - System.Func f = $$delegate(int _, string _) { return 1; }; + System.Func f = [||]delegate(int _, string _) { return 1; }; } }"; await TestMissingAsync(markup); diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 0576f43a0118a..7f234058f7338 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -1508,6 +1508,18 @@ public async Task Parameters() await VerifyItemExistsAsync(@"class c { void M(string args) { $$", "args"); } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task LambdaDiscardParameters() + { + await VerifyItemIsAbsentAsync(@"class C { void M() { System.Func f = (int _, string _) => 1 + $$", "_"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task AnonymousMethodDiscardParameters() + { + await VerifyItemIsAbsentAsync(@"class C { void M() { System.Func f = delegate(int _, string _) { return 1 + $$ }; } }", "_"); + } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task CommonTypesInNewExpressionContext() { From 07739c6acd9da32a80b836787a1b113e29beb7f3 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 12 Nov 2019 15:55:36 -0800 Subject: [PATCH 26/27] Add InlineRename test --- .../Semantics/LambdaDiscardParametersTests.cs | 30 +++++++++++++++++++ .../Test2/Rename/InlineRenameTests.vb | 24 +++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index 4fbf162f2fcaf..e7c242a16488c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -538,5 +538,35 @@ public static void Main() Assert.NotNull(parameterSymbol2); Assert.False(parameterSymbol2.IsDiscard); } + + [Fact] + public void DiscardParameters_Shadowing() + { + var comp = CreateCompilation(@" +using System; +public class C +{ + public static void M() + { + Action f1 = (_) => + { + _.ToString(); // ok + Action g2 = (_) => _.ToString(); // ok + }; + + Action f2 = (_, _) => + { + _.ToString(); // error + Action g2 = (_) => _.ToString(); // ok + }; + } +}"); + + comp.VerifyDiagnostics( + // (15,14): error CS0103: The name '_' does not exist in the current context + // _.ToString(); // error + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(15, 14) + ); + } } } diff --git a/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb b/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb index 6ee284ff7c735..665731039964f 100644 --- a/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb +++ b/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb @@ -56,6 +56,30 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Function + + + Public Async Function RenameLambdaDiscard() As Task + Using workspace = CreateWorkspaceWithWaiter( + + + f = (int _, string [|$$_|]) => { _ = null; return 1; }; + } +} + ]]> + + ) + + Await VerifyRenameOptionChangedSessionCommit(workspace, originalTextToRename:="_", renameTextPrefix:="change", renameOverloads:=True) + VerifyFileName(workspace, "Test1") + End Using + End Function + From 8f95c6b2b64ec909ca6c27289a7aa9bdcf1b52b6 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 12 Nov 2019 15:57:51 -0800 Subject: [PATCH 27/27] Typo --- .../Test/Semantic/Semantics/LambdaDiscardParametersTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs index e7c242a16488c..1c93f0abf2bd5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaDiscardParametersTests.cs @@ -563,9 +563,9 @@ public static void M() }"); comp.VerifyDiagnostics( - // (15,14): error CS0103: The name '_' does not exist in the current context - // _.ToString(); // error - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(15, 14) + // (15,13): error CS0103: The name '_' does not exist in the current context + // _.ToString(); // error + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(15, 13) ); } }