diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index 480c332da4331..064045ec7866f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -462,17 +462,17 @@ private static TypeSymbol MakeMergedTupleType(ArrayBuilder), compilation: compilation, diagnostics: diagnostics, - shouldCheckConstraints: false); + shouldCheckConstraints: false, + errorPositions: default(ImmutableArray)); } private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax, ArrayBuilder variables, DiagnosticBag diagnostics, bool hasErrors) { - bool inferNames = this.Compilation.LanguageVersion.InferTupleElementNames(); int count = variables.Count; var valuesBuilder = ArrayBuilder.GetInstance(count); var typesBuilder = ArrayBuilder.GetInstance(count); var locationsBuilder = ArrayBuilder.GetInstance(count); - var namesBuilder = inferNames ? ArrayBuilder.GetInstance(count) : null; + var namesBuilder = ArrayBuilder.GetInstance(count); foreach (var variable in variables) { if (variable.HasNestedVariables) @@ -480,43 +480,35 @@ private BoundTupleLiteral DeconstructionVariablesAsTuple(CSharpSyntaxNode syntax var nestedTuple = DeconstructionVariablesAsTuple(variable.Syntax, variable.NestedVariables, diagnostics, hasErrors); valuesBuilder.Add(nestedTuple); typesBuilder.Add(nestedTuple.Type); - if (inferNames) - { - namesBuilder.Add(null); - } + namesBuilder.Add(null); } else { var single = variable.Single; valuesBuilder.Add(single); typesBuilder.Add(single.Type); - if (inferNames) - { - namesBuilder.Add(ExtractDeconstructResultElementName(single)); - } + namesBuilder.Add(ExtractDeconstructResultElementName(single)); } locationsBuilder.Add(variable.Syntax.Location); } - ImmutableArray tupleNames; - if (inferNames) - { - var uniqueFieldNames = PooledHashSet.GetInstance(); - RemoveDuplicateInferredTupleNames(namesBuilder, uniqueFieldNames); - uniqueFieldNames.Free(); - tupleNames = namesBuilder.ToImmutableAndFree(); - } - else - { - tupleNames = default(ImmutableArray); - } + var uniqueFieldNames = PooledHashSet.GetInstance(); + RemoveDuplicateInferredTupleNames(namesBuilder, uniqueFieldNames); + uniqueFieldNames.Free(); + ImmutableArray tupleNames = namesBuilder.ToImmutableAndFree(); + + bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); + var inferredPositions = tupleNames.SelectAsArray(n => n != null); var type = TupleTypeSymbol.Create(syntax.Location, typesBuilder.ToImmutableAndFree(), locationsBuilder.ToImmutableAndFree(), tupleNames, this.Compilation, - shouldCheckConstraints: !hasErrors, syntax: syntax, diagnostics: hasErrors ? null : diagnostics); + shouldCheckConstraints: !hasErrors, + errorPositions: disallowInferredNames ? inferredPositions : default(ImmutableArray), + syntax: syntax, diagnostics: hasErrors ? null : diagnostics); - var inferredPositions = inferNames ? tupleNames.SelectAsArray(n => n != null) : default(ImmutableArray); + // Always track the inferred positions in the bound node, so that conversions don't produce a warning + // for "dropped names" on tuple literal when the name was inferred. return new BoundTupleLiteral(syntax, tupleNames, inferredPositions, arguments: valuesBuilder.ToImmutableAndFree(), type: type); } @@ -528,29 +520,7 @@ private static string ExtractDeconstructResultElementName(BoundExpression expres return null; } - SyntaxNode variableSyntax = expression.Syntax; - SyntaxToken nameToken; - if (variableSyntax.Kind() == SyntaxKind.SingleVariableDesignation) - { - nameToken = ((SingleVariableDesignationSyntax)variableSyntax).Identifier; - } - else if (variableSyntax.Kind() == SyntaxKind.DeclarationExpression) - { - var declaration = (DeclarationExpressionSyntax)variableSyntax; - nameToken = ((SingleVariableDesignationSyntax)declaration.Designation).Identifier; - } - else - { - nameToken = ((ExpressionSyntax)variableSyntax).ExtractAnonymousTypeMemberName(); - } - - string name = nameToken.ValueText; - if (name == null || TupleTypeSymbol.IsElementNameReserved(name) != -1) - { - return null; - } - - return name; + return InferTupleElementName(expression.Syntax); } /// diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index a6237b9ee3494..7b3587c586de9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -742,23 +742,37 @@ private BoundExpression BindDeclarationVariables(TypeSymbol declType, VariableDe case SyntaxKind.ParenthesizedVariableDesignation: { var tuple = (ParenthesizedVariableDesignationSyntax)node; - var builder = ArrayBuilder.GetInstance(tuple.Variables.Count); + int count = tuple.Variables.Count; + var builder = ArrayBuilder.GetInstance(count); + var namesBuilder = ArrayBuilder.GetInstance(count); + foreach (var n in tuple.Variables) { builder.Add(BindDeclarationVariables(declType, n, n, diagnostics)); + namesBuilder.Add(InferTupleElementName(n)); } var subExpressions = builder.ToImmutableAndFree(); + var uniqueFieldNames = PooledHashSet.GetInstance(); + RemoveDuplicateInferredTupleNames(namesBuilder, uniqueFieldNames); + uniqueFieldNames.Free(); + + var tupleNames = namesBuilder.ToImmutableAndFree(); + var inferredPositions = tupleNames.SelectAsArray(n => n != null); + bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); + // We will not check constraints at this point as this code path // is failure-only and the caller is expected to produce a diagnostic. var tupleType = TupleTypeSymbol.Create( null, subExpressions.SelectAsArray(e => e.Type), default(ImmutableArray), - default(ImmutableArray), + tupleNames, Compilation, - shouldCheckConstraints: false); - return new BoundTupleLiteral(syntax, default(ImmutableArray), default(ImmutableArray), subExpressions, tupleType); + shouldCheckConstraints: false, + errorPositions: disallowInferredNames ? inferredPositions : default(ImmutableArray)); + + return new BoundTupleLiteral(syntax, default(ImmutableArray), inferredPositions, subExpressions, tupleType); } default: throw ExceptionUtilities.UnexpectedValue(node.Kind()); @@ -787,8 +801,7 @@ private BoundExpression BindTupleExpression(TupleExpressionSyntax node, Diagnost var elementLocations = ArrayBuilder.GetInstance(arguments.Count); // prepare names - var (elementNames, inferredPositions, hasErrors) = ExtractTupleElementNames(arguments, diagnostics, - withInference: this.Compilation.LanguageVersion.InferTupleElementNames()); + var (elementNames, inferredPositions, hasErrors) = ExtractTupleElementNames(arguments, diagnostics); // prepare types and locations for (int i = 0; i < numElements; i++) @@ -831,19 +844,24 @@ private BoundExpression BindTupleExpression(TupleExpressionSyntax node, Diagnost if (hasNaturalType) { + bool disallowInferredNames = this.Compilation.LanguageVersion.DisallowInferredTupleElementNames(); + tupleTypeOpt = TupleTypeSymbol.Create(node.Location, elements, locations, elementNames, - this.Compilation, syntax: node, diagnostics: diagnostics, shouldCheckConstraints: true); + this.Compilation, syntax: node, diagnostics: diagnostics, shouldCheckConstraints: true, + errorPositions: disallowInferredNames ? inferredPositions : default(ImmutableArray)); } else { TupleTypeSymbol.VerifyTupleTypePresent(elements.Length, node, this.Compilation, diagnostics); } + // Always track the inferred positions in the bound node, so that conversions don't produce a warning + // for "dropped names" on tuple literal when the name was inferred. return new BoundTupleLiteral(node, elementNames, inferredPositions, boundArguments.ToImmutableAndFree(), tupleTypeOpt, hasErrors); } private static (ImmutableArray elementNamesArray, ImmutableArray inferredArray, bool hasErrors) ExtractTupleElementNames( - SeparatedSyntaxList arguments, DiagnosticBag diagnostics, bool withInference) + SeparatedSyntaxList arguments, DiagnosticBag diagnostics) { bool hasErrors = false; int numElements = arguments.Count; @@ -868,7 +886,7 @@ private static (ImmutableArray elementNamesArray, ImmutableArray i hasErrors = true; } } - else if (withInference) + else { inferredName = InferTupleElementName(argumentSyntax.Expression); } @@ -954,19 +972,17 @@ private static void RemoveDuplicateInferredTupleNames(ArrayBuilder infer toRemove.Free(); } - private static string InferTupleElementName(ExpressionSyntax element) + private static string InferTupleElementName(SyntaxNode syntax) { - SyntaxToken nameToken = element.ExtractAnonymousTypeMemberName(); - if (nameToken.Kind() == SyntaxKind.IdentifierToken) + string name = syntax.TryGetInferredMemberName(); + + // Reserved names are never candidates to be inferred names, at any position + if (name == null || TupleTypeSymbol.IsElementNameReserved(name) != -1) { - string name = nameToken.ValueText; - // Reserved names are never candidates to be inferred names, at any position - if (TupleTypeSymbol.IsElementNameReserved(name) == -1) - { - return name; - } + return null; } - return null; + + return name; } private BoundExpression BindRefValue(RefValueExpressionSyntax node, DiagnosticBag diagnostics) @@ -5540,12 +5556,6 @@ private void BindMemberAccessReportError( { Error(diagnostics, ErrorCode.ERR_NoSuchMemberOrExtensionNeedUsing, name, boundLeft.Type, plainName, "System"); } - else if (boundLeft.Type.IsTupleType && !Compilation.LanguageVersion.InferTupleElementNames() - && IsMissingMemberAnInferredTupleName(boundLeft, plainName)) - { - Error(diagnostics, ErrorCode.ERR_TupleInferredNamesNotAvailable, name, boundLeft.Type, plainName, - new CSharpRequiredLanguageVersion(LanguageVersion.CSharp7_1)); - } else { Error(diagnostics, ErrorCode.ERR_NoSuchMemberOrExtension, name, boundLeft.Type, plainName); @@ -5553,45 +5563,6 @@ private void BindMemberAccessReportError( } } - /// - /// When reporting a missing member error on a tuple type, determine whether that member - /// could be an element with an inferred name (had the language version been above 7.1). - /// - private static bool IsMissingMemberAnInferredTupleName(BoundExpression boundLeft, string plainName) - { - Debug.Assert(boundLeft.Type.IsTupleType); - - var declarations = boundLeft.Type.DeclaringSyntaxReferences; - if (declarations.Length != 1) - { - return false; - } - - var syntax = declarations[0].GetSyntax(); - if (syntax.Kind() != SyntaxKind.TupleExpression) - { - return false; - } - - var arguments = ((TupleExpressionSyntax)syntax).Arguments; - var (elementNames, inferredPositions, _) = ExtractTupleElementNames(arguments, diagnostics: null, withInference: true); - - if (elementNames.IsDefault || inferredPositions.IsDefault) - { - return false; - } - - for (int i = 0; i < arguments.Count; i++) - { - if (elementNames[i]?.Equals(plainName, StringComparison.Ordinal) == true && inferredPositions[i]) - { - return true; - } - } - - return false; - } - private bool WouldUsingSystemFindExtension(TypeSymbol receiver, string methodName) { // we have a special case to make the diagnostic for await expressions more clear for Windows: diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 2c935cfc6f0df..d02e32d22e28e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -480,8 +480,9 @@ private TypeSymbol BindTupleType(TupleTypeSyntax syntax, DiagnosticBag diagnosti elementNames.ToImmutableAndFree(), this.Compilation, this.ShouldCheckConstraints, - syntax, - diagnostics); + errorPositions: default(ImmutableArray), + syntax: syntax, + diagnostics: diagnostics); } private static void CollectTupleFieldMemberName(string name, int elementIndex, int tupleSize, ref ArrayBuilder elementNames) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 857dd1780e487..0431b81ebdfb2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -9062,7 +9062,7 @@ internal static string ERR_TupleElementNamesInDeconstruction { } /// - /// Looks up a localized string similar to Tuple type '{0}' does not have an explicitly named element '{1}'. Please use language version {2} or greater to access a unnamed element by its inferred name.. + /// Looks up a localized string similar to Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name.. /// internal static string ERR_TupleInferredNamesNotAvailable { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index c0bd22fda1ed6..bd844df0917dd 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5060,7 +5060,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Compiler version: '{0}'. Language version: {1}. - Tuple type '{0}' does not have an explicitly named element '{1}'. Please use language version {2} or greater to access a unnamed element by its inferred name. + Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name. Did you mean to use the default switch label (`default:`) rather than `case default:`? If you really mean to use the default literal, consider `case (default):` or another literal (`case 0:` or `case null:`) as appropriate. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index a163e9ecc6b49..e30e81fa20749 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2733,7 +2733,8 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol( elementLocations: elementLocations, elementNames: elementNames, compilation: this, - shouldCheckConstraints: false); + shouldCheckConstraints: false, + errorPositions: default(ImmutableArray)); } protected override INamedTypeSymbol CommonCreateTupleTypeSymbol( diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index fa798c7c8cb2c..c80e0e7c7a27c 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -128,6 +128,7 @@ internal enum MessageID IDS_FeatureExpressionBodiedDeOrConstructor = MessageBase + 12716, IDS_ThrowExpression = MessageBase + 12717, IDS_FeatureDefaultLiteral = MessageBase + 12718, + IDS_FeatureInferredTupleNames = MessageBase + 12719, } // Message IDs may refer to strings that need to be localized. @@ -186,6 +187,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) { // C# 7.1 features. case MessageID.IDS_FeatureDefaultLiteral: + case MessageID.IDS_FeatureInferredTupleNames: return LanguageVersion.CSharp7_1; // C# 7 features. diff --git a/src/Compilers/CSharp/Portable/LanguageVersion.cs b/src/Compilers/CSharp/Portable/LanguageVersion.cs index 4545ee900d016..48eee24705b8e 100644 --- a/src/Compilers/CSharp/Portable/LanguageVersion.cs +++ b/src/Compilers/CSharp/Portable/LanguageVersion.cs @@ -258,9 +258,9 @@ public static LanguageVersion MapSpecifiedToEffectiveVersion(this LanguageVersio } /// Inference of tuple element names was added in C# 7.1 - internal static bool InferTupleElementNames(this LanguageVersion self) + internal static bool DisallowInferredTupleElementNames(this LanguageVersion self) { - return self >= LanguageVersion.CSharp7_1; + return self < MessageID.IDS_FeatureInferredTupleNames.RequiredVersion(); } } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs index 14272a7dd5d5b..94cada8ffe998 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs @@ -99,7 +99,7 @@ private BoundTupleLiteral ApplyDeconstructionConversion(ArrayBuilder e.Type), elementLocations: default(ImmutableArray), elementNames: default(ImmutableArray), - compilation: _compilation, shouldCheckConstraints: false); + compilation: _compilation, shouldCheckConstraints: false, errorPositions: default(ImmutableArray)); return new BoundTupleLiteral(right.Syntax, default(ImmutableArray), default(ImmutableArray), builder.ToImmutableAndFree(), tupleType); } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index e22257cff2b71..74993f0e4e468 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -6,4 +6,4 @@ static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.MapSpecifiedToEffectiv static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.ToDisplayString(this Microsoft.CodeAnalysis.CSharp.LanguageVersion version) -> string static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.TryParse(this string version, out Microsoft.CodeAnalysis.CSharp.LanguageVersion result) -> bool static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsReservedTupleElementName(string elementName) -> bool -static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.TryGetInferredMemberName(this Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> string \ No newline at end of file +static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.TryGetInferredMemberName(this Microsoft.CodeAnalysis.SyntaxNode syntax) -> string \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs index 815ab4b99e3c3..1198bc5f25b02 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs @@ -250,13 +250,15 @@ public override FieldSymbol CorrespondingTupleField internal sealed class TupleVirtualElementFieldSymbol : TupleElementFieldSymbol { private readonly string _name; + private readonly bool _cannotUse; // With LanguageVersion 7, we will produce named elements that should not be used public TupleVirtualElementFieldSymbol( TupleTypeSymbol container, FieldSymbol underlyingField, - string name, + string name, int tupleElementIndex, Location location, + bool cannotUse, bool isImplicitlyDeclared, TupleElementFieldSymbol correspondingDefaultFieldOpt) @@ -267,6 +269,18 @@ public TupleVirtualElementFieldSymbol( "fields that map directly to underlying should not be represented by " + nameof(TupleVirtualElementFieldSymbol)); _name = name; + _cannotUse = cannotUse; + } + + internal override DiagnosticInfo GetUseSiteDiagnostic() + { + if (_cannotUse) + { + return new CSDiagnosticInfo(ErrorCode.ERR_TupleInferredNamesNotAvailable, _name, + new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureInferredTupleNames.RequiredVersion())); + } + + return base.GetUseSiteDiagnostic(); } public override string Name diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs index bfbd8e9f7b929..2cf4da99dc599 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs @@ -33,6 +33,13 @@ internal sealed class TupleTypeSymbol : WrappedNamedTypeSymbol /// private readonly ImmutableArray _elementNames; + /// + /// Which element names were inferred and therefore cannot be used. + /// If none of the element names were inferred, or inferred names can be used (no tracking necessary), leave as default. + /// This information is ignored in type equality and comparison. + /// + private readonly ImmutableArray _errorPositions; + /// /// Element types. /// @@ -47,13 +54,15 @@ internal sealed class TupleTypeSymbol : WrappedNamedTypeSymbol internal const string TupleTypeName = "ValueTuple"; internal const string RestFieldName = "Rest"; - private TupleTypeSymbol(Location locationOpt, NamedTypeSymbol underlyingType, ImmutableArray elementLocations, ImmutableArray elementNames, ImmutableArray elementTypes) + private TupleTypeSymbol(Location locationOpt, NamedTypeSymbol underlyingType, ImmutableArray elementLocations, + ImmutableArray elementNames, ImmutableArray elementTypes, ImmutableArray inferredNamesPositions) : this(locationOpt == null ? ImmutableArray.Empty : ImmutableArray.Create(locationOpt), - underlyingType, elementLocations, elementNames, elementTypes) + underlyingType, elementLocations, elementNames, elementTypes, inferredNamesPositions) { } - private TupleTypeSymbol(ImmutableArray locations, NamedTypeSymbol underlyingType, ImmutableArray elementLocations, ImmutableArray elementNames, ImmutableArray elementTypes) + private TupleTypeSymbol(ImmutableArray locations, NamedTypeSymbol underlyingType, ImmutableArray elementLocations, + ImmutableArray elementNames, ImmutableArray elementTypes, ImmutableArray errorPositions) : base(underlyingType) { Debug.Assert(elementLocations.IsDefault || elementLocations.Length == elementTypes.Length); @@ -64,6 +73,7 @@ private TupleTypeSymbol(ImmutableArray locations, NamedTypeSymbol unde _elementNames = elementNames; _elementTypes = elementTypes; _locations = locations; + _errorPositions = errorPositions; } /// @@ -76,6 +86,7 @@ internal static NamedTypeSymbol Create( ImmutableArray elementNames, CSharpCompilation compilation, bool shouldCheckConstraints, + ImmutableArray errorPositions, CSharpSyntaxNode syntax = null, DiagnosticBag diagnostics = null) { @@ -97,7 +108,7 @@ internal static NamedTypeSymbol Create( Emit.NoPia.EmbeddedTypesManager.IsValidEmbeddableType(underlyingType, syntax, diagnostics); } - var constructedType = Create(underlyingType, elementNames, locationOpt, elementLocations); + var constructedType = Create(underlyingType, elementNames, errorPositions, locationOpt, elementLocations); if (shouldCheckConstraints) { constructedType.CheckConstraints(compilation.Conversions, syntax, elementLocations, compilation, diagnostics); @@ -108,16 +119,19 @@ internal static NamedTypeSymbol Create( public static TupleTypeSymbol Create(NamedTypeSymbol tupleCompatibleType, ImmutableArray elementNames = default(ImmutableArray), + ImmutableArray errorPositions = default(ImmutableArray), Location locationOpt = null, ImmutableArray elementLocations = default(ImmutableArray)) { return Create(locationOpt == null ? ImmutableArray.Empty : ImmutableArray.Create(locationOpt), tupleCompatibleType, elementLocations, - elementNames); + elementNames, + errorPositions); } - public static TupleTypeSymbol Create(ImmutableArray locations, NamedTypeSymbol tupleCompatibleType, ImmutableArray elementLocations, ImmutableArray elementNames) + public static TupleTypeSymbol Create(ImmutableArray locations, NamedTypeSymbol tupleCompatibleType, + ImmutableArray elementLocations, ImmutableArray elementNames, ImmutableArray errorPositions) { Debug.Assert(tupleCompatibleType.IsTupleCompatible()); @@ -139,7 +153,7 @@ public static TupleTypeSymbol Create(ImmutableArray locations, NamedTy elementTypes = tupleCompatibleType.TypeArgumentsNoUseSiteDiagnostics; } - return new TupleTypeSymbol(locations, tupleCompatibleType, elementLocations, elementNames, elementTypes); + return new TupleTypeSymbol(locations, tupleCompatibleType, elementLocations, elementNames, elementTypes, errorPositions); } /// @@ -211,12 +225,13 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType) { Debug.Assert(!newUnderlyingType.IsTupleType && newUnderlyingType.IsTupleOrCompatibleWithTupleOfCardinality(_elementTypes.Length)); - return Create(_locations, newUnderlyingType, _elementLocations, _elementNames); + return Create(_locations, newUnderlyingType, _elementLocations, _elementNames, _errorPositions); } /// /// Copy this tuple, but modify it to use the new element names. /// Also applies new location of the whole tuple as well as each element. + /// Drops the inferred positions. /// internal TupleTypeSymbol WithElementNames(ImmutableArray newElementNames, Location newLocation, @@ -224,7 +239,7 @@ internal TupleTypeSymbol WithElementNames(ImmutableArray newElementNames { Debug.Assert(newElementNames.IsDefault || this._elementTypes.Length == newElementNames.Length); - return new TupleTypeSymbol(newLocation, _underlyingType, newElementLocations, newElementNames, _elementTypes); + return new TupleTypeSymbol(newLocation, _underlyingType, newElementLocations, newElementNames, _elementTypes, default(ImmutableArray)); } /// @@ -822,6 +837,7 @@ private ImmutableArray CreateMembers() defaultName, tupleFieldIndex, location, + cannotUse: false, isImplicitlyDeclared: defaultImplicitlyDeclared, correspondingDefaultFieldOpt: null); } @@ -842,6 +858,8 @@ private ImmutableArray CreateMembers() if (defaultImplicitlyDeclared && !String.IsNullOrEmpty(providedName)) { + var isError = _errorPositions.IsDefault ? false : _errorPositions[tupleFieldIndex]; + // The name given doesn't match the default name Item8, etc. // Add a virtual field with the given name members.Add(new TupleVirtualElementFieldSymbol(this, @@ -849,6 +867,7 @@ private ImmutableArray CreateMembers() providedName, tupleFieldIndex, location, + cannotUse: isError, isImplicitlyDeclared: false, correspondingDefaultFieldOpt: defaultTupleField)); } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index 78b00bbc45d85..77b3fb71cd12f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -431,15 +431,41 @@ internal static bool IsDeclarationExpressionType(SyntaxNode node, out Declaratio /// Given an initializer expression infer the name of anonymous property or tuple element. /// Returns null if unsuccessful /// - public static string TryGetInferredMemberName(this ExpressionSyntax expression) + public static string TryGetInferredMemberName(this SyntaxNode syntax) { - if (expression == null) + SyntaxToken nameToken; + switch (syntax.Kind()) { - return null; + case SyntaxKind.SingleVariableDesignation: + nameToken = ((SingleVariableDesignationSyntax)syntax).Identifier; + break; + + case SyntaxKind.DeclarationExpression: + var declaration = (DeclarationExpressionSyntax)syntax; + var designationKind = declaration.Designation.Kind(); + if (designationKind == SyntaxKind.ParenthesizedVariableDesignation || + designationKind == SyntaxKind.DiscardDesignation) + { + return null; + } + + nameToken = ((SingleVariableDesignationSyntax)declaration.Designation).Identifier; + break; + + case SyntaxKind.ParenthesizedVariableDesignation: + case SyntaxKind.DiscardDesignation: + return null; + + default: + if (syntax is ExpressionSyntax expr) + { + nameToken = expr.ExtractAnonymousTypeMemberName(); + break; + } + return null; } - var nameToken = expression.ExtractAnonymousTypeMemberName(); - return nameToken.Kind() == SyntaxKind.IdentifierToken ? nameToken.ValueText : null; + return nameToken.ValueText; } /// diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs index 739befcac0841..bf81f5634deff 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs @@ -91,7 +91,7 @@ public void Deconstruct(out int a, out string b) var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal(@"(x, y)", lhs.ToString()); - Assert.Equal("(System.Int64, System.String)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.Int64 x, System.String y)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); var right = tree.GetRoot().DescendantNodes().OfType().Single(); Assert.Equal(@"new C()", right.ToString()); @@ -1030,7 +1030,7 @@ public void Deconstruct(out int a, out int b) var x = nodes.OfType().ElementAt(2); - Assert.Equal("(System.Int32, System.Int32) z", model.GetDeclaredSymbol(x).ToTestDisplayString()); + Assert.Equal("(System.Int32 x, System.Int32 y) z", model.GetDeclaredSymbol(x).ToTestDisplayString()); }; var comp = CompileAndVerify(source, additionalRefs: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); @@ -1057,6 +1057,43 @@ .locals init (System.ValueTuple V_0, //z }"); } + [Fact] + public void ValueTupleReturnIsEmittedIfUsed_WithCSharp7_1() + { + string source = @" +class C +{ + public static void Main() + { + int x, y; + var z = ((x, y) = new C()); + z.ToString(); + } + + public void Deconstruct(out int a, out int b) + { + a = 1; + b = 2; + } +} +"; + Action validator = module => + { + var sourceModule = (SourceModuleSymbol)module; + var compilation = sourceModule.DeclaringCompilation; + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var x = nodes.OfType().ElementAt(2); + + Assert.Equal("(System.Int32 x, System.Int32 y) z", model.GetDeclaredSymbol(x).ToTestDisplayString()); + }; + var comp = CompileAndVerify(source, additionalRefs: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, + sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + [Fact] public void ValueTupleReturnMissingMemberWithCSharp7() { @@ -1075,9 +1112,9 @@ public void M() var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyDiagnostics( - // (8,37): error CS8305: Tuple type '(int, int)' does not have an explicitly named element 'x'. Please use language version 7.1 or greater to access a unnamed element by its inferred name. + // (8,37): error CS8305: Tuple element name 'x' is inferred. Please use language version 7.1 or greater to access an element by its inferred name. // System.Console.Write(nested.x); - Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "x").WithArguments("(int, int)", "x", "7.1").WithLocation(8, 37) + Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "x").WithArguments("x", "7.1").WithLocation(8, 37) ); } @@ -1838,7 +1875,7 @@ static void Main() var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal(@"(x, (y, z))", lhs.ToString()); - Assert.Equal("(System.Int32, (System.String, System.String))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 x, (System.String y, System.String z))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); }; var comp = CompileAndVerify(source, expectedOutput: "1 a b", additionalRefs: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, sourceSymbolValidator: validator); @@ -2184,7 +2221,7 @@ static void Main() var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal(@"var (x1, (x2, x3))", lhs.ToString()); - Assert.Equal("(System.Int32, (System.Int32, System.String))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var lhsNested = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); @@ -2209,6 +2246,108 @@ static void Main() comp.VerifyDiagnostics(); } + [Fact] + public void NestedVarDeconstructionDeclaration_WithCSharp7_1() + { + string source = @" +class C +{ + static void Main() + { + (int x1, var (x2, (x3, x4)), var x5) = (1, (2, (3, ""hello"")), 5); + System.Console.WriteLine($""{x1} {x2} {x3} {x4} {x5}""); + } +} +"; + + Action validator = (ModuleSymbol module) => + { + var sourceModule = (SourceModuleSymbol)module; + var compilation = sourceModule.DeclaringCompilation; + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var lhs = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal(@"(int x1, var (x2, (x3, x4)), var x5)", lhs.ToString()); + Assert.Equal("(System.Int32 x1, (System.Int32 x2, (System.Int32 x3, System.String x4)), System.Int32 x5)", + model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(lhs).Symbol); + + var x234 = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); + Assert.Equal(@"var (x2, (x3, x4))", x234.ToString()); + Assert.Equal("(System.Int32 x2, (System.Int32 x3, System.String x4))", model.GetTypeInfo(x234).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(x234).Symbol); + + var x34 = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); + Assert.Equal(@"(x3, x4)", x34.ToString()); + Assert.Null(model.GetTypeInfo(x34).Type); + Assert.Null(model.GetSymbolInfo(x34).Symbol); + + var x1 = GetDeconstructionVariable(tree, "x1"); + var x1Ref = GetReference(tree, "x1"); + VerifyModelForDeconstructionLocal(model, x1, x1Ref); + + var x2 = GetDeconstructionVariable(tree, "x2"); + var x2Ref = GetReference(tree, "x2"); + VerifyModelForDeconstructionLocal(model, x2, x2Ref); + + var x3 = GetDeconstructionVariable(tree, "x3"); + var x3Ref = GetReference(tree, "x3"); + VerifyModelForDeconstructionLocal(model, x3, x3Ref); + + var x4 = GetDeconstructionVariable(tree, "x4"); + var x4Ref = GetReference(tree, "x4"); + VerifyModelForDeconstructionLocal(model, x4, x4Ref); + + var x5 = GetDeconstructionVariable(tree, "x5"); + var x5Ref = GetReference(tree, "x5"); + VerifyModelForDeconstructionLocal(model, x5, x5Ref); + }; + + var comp = CompileAndVerify(source, expectedOutput: "1 2 3 hello 5", additionalRefs: s_valueTupleRefs, + sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + + [Fact] + public void NestedVarDeconstructionAssignment_WithCSharp7_1() + { + string source = @" +class C +{ + static void Main() + { + int x1, x2, x3; + (x1, (x2, x3)) = (1, (2, 3)); + System.Console.WriteLine($""{x1} {x2} {x3}""); + } +} +"; + + Action validator = (ModuleSymbol module) => + { + var sourceModule = (SourceModuleSymbol)module; + var compilation = sourceModule.DeclaringCompilation; + var tree = compilation.SyntaxTrees.First(); + var model = compilation.GetSemanticModel(tree); + + var x123 = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal(@"(x1, (x2, x3))", x123.ToString()); + Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.Int32 x3))", + model.GetTypeInfo(x123).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(x123).Symbol); + + var x23 = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); + Assert.Equal(@"(x2, x3)", x23.ToString()); + Assert.Equal("(System.Int32 x2, System.Int32 x3)", model.GetTypeInfo(x23).Type.ToTestDisplayString()); + Assert.Null(model.GetSymbolInfo(x23).Symbol); + }; + + var comp = CompileAndVerify(source, expectedOutput: "1 2 3", additionalRefs: s_valueTupleRefs, + sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics(); + } + [Fact] public void NestedVarDeconstructionDeclaration2() { @@ -2232,12 +2371,12 @@ static void Main() var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal("(var x1, var (x2, x3))", lhs.ToString()); - Assert.Equal("(System.Int32, (System.Int32, System.String))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 x1, (System.Int32 x2, System.String x3))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhs).Symbol); var lhsNested = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); Assert.Equal("var (x2, x3)", lhsNested.ToString()); - Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(lhsNested).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 x2, System.String x3)", model.GetTypeInfo(lhsNested).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(lhsNested).Symbol); var x1 = GetDeconstructionVariable(tree, "x1"); @@ -2392,7 +2531,7 @@ static void Main() var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal(@"(string x1, byte x2, var x3)", lhs.ToString()); - Assert.Equal("(System.String, System.Byte, System.Int32)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.String x1, System.Byte x2, System.Int32 x3)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); var literal = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); Assert.Equal(@"(null, 2, 3)", literal.ToString()); @@ -2428,7 +2567,7 @@ static void Main() var lhs = tree.GetRoot().DescendantNodes().OfType().First(); Assert.Equal(@"(string x1, var x2)", lhs.ToString()); - Assert.Equal("(System.String, (System.Int32, System.Int32))", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); + Assert.Equal("(System.String x1, (System.Int32, System.Int32) x2)", model.GetTypeInfo(lhs).Type.ToTestDisplayString()); var literal = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); Assert.Equal(@"(null, (1, 2))", literal.ToString()); @@ -2985,12 +3124,17 @@ static void Main() // extra check on var var x12Var = (DeclarationExpressionSyntax)x1.Parent.Parent; Assert.Equal("var", x12Var.Type.ToString()); + Assert.Equal("(System.Int32 x1, System.Int32 x2)", model.GetTypeInfo(x12Var).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(x12Var.Type).Symbol); // The var in `var (x1, x2)` has no symbol }; var comp = CompileAndVerify(source, expectedOutput: "1 2", additionalRefs: s_valueTupleRefs, sourceSymbolValidator: validator); comp.VerifyDiagnostics(); + var comp7_1 = CompileAndVerify(source, expectedOutput: "1 2", additionalRefs: s_valueTupleRefs, + sourceSymbolValidator: validator, parseOptions: TestOptions.Regular7_1); + comp7_1.VerifyDiagnostics(); + comp.VerifyIL("C.Main", @"{ // Code size 70 (0x46) @@ -4851,7 +4995,7 @@ static void Main() var discard3 = GetDiscardDesignations(tree).ElementAt(2); var declaration3 = (DeclarationExpressionSyntax)discard3.Parent.Parent; Assert.Equal("var (_, z)", declaration3.ToString()); - Assert.Equal("(C, System.Int32)", model.GetTypeInfo(declaration3).Type.ToTestDisplayString()); + Assert.Equal("(C, System.Int32 z)", model.GetTypeInfo(declaration3).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(declaration3).Symbol); } @@ -5065,7 +5209,7 @@ static void Main() var tuple1 = (TupleExpressionSyntax)discard1.Parent.Parent; Assert.Equal("(_, x)", tuple1.ToString()); - Assert.Equal("(System.Int64, System.Int32)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); + Assert.Equal("(System.Int64, System.Int32 x)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(tuple1).Symbol); } @@ -5095,7 +5239,7 @@ static void Main() Assert.Null(model.GetDeclaredSymbol(discard1)); var tuple1 = (DeclarationExpressionSyntax)discard1.Parent.Parent; Assert.Equal("var (_, x)", tuple1.ToString()); - Assert.Equal("(System.Int32, System.Int32)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32, System.Int32 x)", model.GetTypeInfo(tuple1).Type.ToTestDisplayString()); } [Fact] @@ -5155,7 +5299,7 @@ static void Main() var declaration1 = (DeclarationExpressionSyntax)discard1.Parent.Parent; Assert.Equal("var (_, x)", declaration1.ToString()); Assert.Null(model.GetTypeInfo(discard1).Type); - Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(declaration1).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32, System.String x)", model.GetTypeInfo(declaration1).Type.ToTestDisplayString()); IdentifierNameSyntax discard2 = GetDiscardIdentifiers(tree).First(); Assert.Equal("(_, (var y, int z))", discard2.Parent.Parent.ToString()); @@ -5164,7 +5308,7 @@ static void Main() var yz = tree.GetRoot().DescendantNodes().OfType().ElementAt(2); Assert.Equal("(var y, int z)", yz.ToString()); - Assert.Equal("(System.String, System.Int32)", model.GetTypeInfo(yz).Type.ToTestDisplayString()); + Assert.Equal("(System.String y, System.Int32 z)", model.GetTypeInfo(yz).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(yz).Symbol); var y = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); @@ -5665,12 +5809,12 @@ public void NestedVarDiscardDeconstructionInScript() Assert.Equal("var (_, x3)", nestedDeclaration.ToString()); Assert.Null(model.GetDeclaredSymbol(nestedDeclaration)); Assert.Null(model.GetDeclaredSymbol(discard2)); - Assert.Equal("(System.Int32, System.Int32)", model.GetTypeInfo(nestedDeclaration).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32, System.Int32 x3)", model.GetTypeInfo(nestedDeclaration).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(nestedDeclaration).Symbol); var tuple = (TupleExpressionSyntax)discard2.Parent.Parent.Parent.Parent; Assert.Equal("(var _, var (_, x3))", tuple.ToString()); - Assert.Equal("(System.String, (System.Int32, System.Int32))", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); + Assert.Equal("(System.String, (System.Int32, System.Int32 x3))", model.GetTypeInfo(tuple).Type.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(tuple).Symbol); }; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 08f70da616d85..4e7771532770f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -3548,11 +3548,11 @@ void M() var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); var yTuple = nodes.OfType().ElementAt(0); - Assert.Equal("(System.Int32, System.Int32, System.Int32 b, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32)", + Assert.Equal("(System.Int32 a, System.Int32, System.Int32 b, System.Int32, System.Int32, System.Int32 e, System.Int32 f, System.Int32, System.Int32, System.Int32)", model.GetTypeInfo(yTuple).Type.ToTestDisplayString()); var zTuple = nodes.OfType().ElementAt(1); - Assert.Equal("(System.Int32 x, System.Int32)", model.GetTypeInfo(zTuple).Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 x, System.Int32 b)", model.GetTypeInfo(zTuple).Type.ToTestDisplayString()); }; var verifier = CompileAndVerify(source, additionalRefs: new[] { MscorlibRef, ValueTupleRef, SystemRuntimeFacadeRef }, sourceSymbolValidator: validator); @@ -3580,15 +3580,141 @@ void M() "; var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( - // (8,32): error CS8305: Tuple type '(int, int)' does not have an explicitly named element 'a'. Please use language version 7.1 or greater to access a unnamed element by its inferred name. + // (8,32): error CS8305: Tuple element name 'a' is inferred. Please use language version 7.1 or greater to access an element by its inferred name. // System.Console.Write(t.a); - Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "a").WithArguments("(int, int)", "a", "7.1").WithLocation(8, 32), + Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "a").WithArguments("a", "7.1").WithLocation(8, 32), // (9,41): error CS1061: '(int, int)' does not contain a definition for 'a' and no extension method 'a' accepting a first argument of type '(int, int)' could be found (are you missing a using directive or an assembly reference?) // System.Console.Write(GetTuple().a); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "a").WithArguments("(int, int)", "a").WithLocation(9, 41) ); } + [Fact] + public void UseSiteDiagnosticOnTupleField() + { + var missing_cs = @"public class Missing { }"; + + var lib_cs = @" +public class C +{ + public static (Missing, int) GetTuple() + { + throw null; + } +} +"; + var source_cs = @" +class D +{ + void M() + { + System.Console.Write(C.GetTuple().Item1); + } +}"; + var missingComp = CreateStandardCompilation(missing_cs, assemblyName: "UseSiteDiagnosticOnTupleField_missingComp"); + missingComp.VerifyDiagnostics(); + + var libComp = CreateStandardCompilation(lib_cs, + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef, missingComp.ToMetadataReference() }); + libComp.VerifyDiagnostics(); + + var comp7 = CreateStandardCompilation(source_cs, + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef, libComp.ToMetadataReference() }, parseOptions: TestOptions.Regular); + + comp7.VerifyDiagnostics( + // (6,30): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(C.GetTuple().Item1); + Diagnostic(ErrorCode.ERR_NoTypeDef, "C.GetTuple").WithArguments("Missing", "UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"), + // (6,43): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(C.GetTuple().Item1); + Diagnostic(ErrorCode.ERR_NoTypeDef, "Item1").WithArguments("Missing", "UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") + ); + + var comp7_1 = CreateStandardCompilation(source_cs, assemblyName: "UseSiteDiagnosticOnTupleField_comp7_1", + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef, libComp.ToMetadataReference() }, parseOptions: TestOptions.Regular7_1); + + comp7_1.VerifyDiagnostics( + // (6,30): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(C.GetTuple().Item1); + Diagnostic(ErrorCode.ERR_NoTypeDef, "C.GetTuple").WithArguments("Missing", "UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"), + // (6,43): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(C.GetTuple().Item1); + Diagnostic(ErrorCode.ERR_NoTypeDef, "Item1").WithArguments("Missing", "UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") + ); + } + + [Fact] + public void UseSiteDiagnosticOnTupleField2() + { + var source_cs = @" +public class C +{ + void M() + { + int a = 1; + var t = (a, 2); + System.Console.Write(t.a); + } +} +namespace System +{ + public struct ValueTuple + { + public ValueTuple(T1 item1, T2 item2) { } + } +} +"; + + var comp7 = CreateStandardCompilation(source_cs, parseOptions: TestOptions.Regular, assemblyName: "UseSiteDiagnosticOnTupleField2_comp7"); + comp7.VerifyDiagnostics( + // (8,32): error CS8128: Member 'Item1' was not found on type 'ValueTuple' from assembly 'UseSiteDiagnosticOnTupleField2_comp7, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(t.a); + Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "a").WithArguments("Item1", "System.ValueTuple", "UseSiteDiagnosticOnTupleField2_comp7, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") + ); + + var comp7_1 = CreateStandardCompilation(source_cs, parseOptions: TestOptions.Regular7_1, assemblyName: "UseSiteDiagnosticOnTupleField2_comp7_1"); + comp7_1.VerifyDiagnostics( + // (8,32): error CS8128: Member 'Item1' was not found on type 'ValueTuple' from assembly 'UseSiteDiagnosticOnTupleField2_comp7_1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // System.Console.Write(t.a); + Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "a").WithArguments("Item1", "System.ValueTuple", "UseSiteDiagnosticOnTupleField2_comp7_1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 32) + ); + } + + [Fact] + public void MissingMemberAccessWithExtensionWithCSharp7() + { + var source = @" +class C +{ + void M() + { + int a = 1; + var t = (a, 2); + System.Console.Write(t.a); + System.Console.Write(t.a()); + System.Console.Write(GetTuple().a); + } + (int, int) GetTuple() + { + return (1, 2); + } +} +public static class Extensions +{ + public static string a(this (int, int) self) { return ""hello""; } +} +"; + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef }); + comp.VerifyDiagnostics( + // (8,32): error CS8305: Tuple element name 'a' is inferred. Please use language version 7.1 or greater to access an element by its inferred name. + // System.Console.Write(t.a); + Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "a").WithArguments("a", "7.1").WithLocation(8, 32), + // (10,30): error CS1503: Argument 1: cannot convert from 'method group' to 'string' + // System.Console.Write(GetTuple().a); + Diagnostic(ErrorCode.ERR_BadArgType, "GetTuple().a").WithArguments("1", "method group", "string") + ); + } + [Fact] public void MissingMemberAccessWithCSharp7_1() { @@ -3599,15 +3725,16 @@ void M() { int a = 1; var t = (a, 2); + System.Console.Write(t.a); System.Console.Write(t.b); } } "; - var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll); + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll, parseOptions: TestOptions.Regular7_1); comp.VerifyDiagnostics( - // (8,32): error CS1061: '(int, int)' does not contain a definition for 'b' and no extension method 'b' accepting a first argument of type '(int, int)' could be found (are you missing a using directive or an assembly reference?) + // (9,32): error CS1061: '(int a, int)' does not contain a definition for 'b' and no extension method 'b' accepting a first argument of type '(int a, int)' could be found (are you missing a using directive or an assembly reference?) // System.Console.Write(t.b); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "b").WithArguments("(int, int)", "b").WithLocation(8, 32) + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "b").WithArguments("(int a, int)", "b").WithLocation(9, 32) ); } @@ -3620,7 +3747,7 @@ class C int e = 5; int f = 6; C instance = null; - void M() + string M() { int a = 1; int b = 3; @@ -3631,6 +3758,16 @@ void M() var z = (x: b, b); System.Console.Write(y); System.Console.Write(z); + return null; + } + + int P + { + set + { + var t = (M(), value); + System.Console.Write(t.value); + } } } "; @@ -3649,6 +3786,9 @@ void M() var zTuple = nodes.OfType().ElementAt(1); Assert.Equal("(System.Int32 x, System.Int32 b)", model.GetTypeInfo(zTuple).Type.ToTestDisplayString()); + + var tTuple = nodes.OfType().ElementAt(2); + Assert.Equal("(System.String, System.Int32 value)", model.GetTypeInfo(tTuple).Type.ToTestDisplayString()); }; var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1), @@ -3763,7 +3903,7 @@ static void Main() } [Fact] - public void InferredNames_ExtensionInvokedInCSharp7ButNotCSharp7_1() + public void InferredNames_ExtensionNowFailsInCSharp7ButNotCSharp7_1() { var source = @" using System; @@ -3781,10 +3921,15 @@ static class Extension } "; - var verifier7 = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7), - additionalRefs: new[] { MscorlibRef, ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef }, - expectedOutput: "extension"); - verifier7.VerifyDiagnostics(); + // When C# 7.0 shipped, no tuple element would be found/inferred, so the extension method was called. + // The C# 7.1 compiler disallows that, even when LanguageVersion is 7.0 + var comp7 = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7), + references: new[] { MscorlibRef, ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef }); + comp7.VerifyDiagnostics( + // (8,16): error CS8305: Tuple element name 'M' is inferred. Please use language version 7.1 or greater to access an element by its inferred name. + // (1, M).M(); + Diagnostic(ErrorCode.ERR_TupleInferredNamesNotAvailable, "M").WithArguments("M", "7.1") + ); var verifier7_1 = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1), additionalRefs: new[] { MscorlibRef, ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef }, @@ -15378,6 +15523,41 @@ static void Main() Assert.Equal(nc.Name.GetLocation(), sym.Symbol.Locations[0]); } + [Fact] + public void GetSymbolInfo_WithInferredName() + { + var source = @" +class C +{ + static void Main() + { + string Bob = ""hello""; + var x1 = (Alice: 1, Bob); + + var Alice = x1.Alice; + var BobCopy = x1.Bob; + } +} +"; + + var tree = Parse(source, options: TestOptions.Regular7_1); + var comp = CreateStandardCompilation(tree, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + comp.VerifyDiagnostics(); + + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var x1Bob = nodes.OfType().ElementAt(1); + Assert.Equal("x1.Bob", x1Bob.ToString()); + var x1Symbol = model.GetSymbolInfo(x1Bob.Expression).Symbol as LocalSymbol; + Assert.Equal("(System.Int32 Alice, System.String Bob)", x1Symbol.Type.ToTestDisplayString()); + var bobField = x1Symbol.Type.GetMember("Bob"); + + Assert.Equal(SymbolKind.Field, bobField.Kind); + var secondElement = nodes.OfType().First().Arguments[1]; + Assert.Equal(secondElement.GetLocation(), bobField.Locations[0]); + } + [Fact] public void CompileTupleLib() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs index 84150c246809e..a4ab885bcfc7f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs @@ -2868,7 +2868,7 @@ private static void StandAlone_01_VerifySemanticModel(CSharpCompilation comp, Lo Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -2919,7 +2919,7 @@ private static void StandAlone_01_VerifySemanticModel(CSharpCompilation comp, Lo var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); typeInfo = model.GetTypeInfo(tuple); - Assert.Equal("((var, var), var, System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c, System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); @@ -3001,7 +3001,7 @@ private static void StandAlone_02_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -3052,7 +3052,7 @@ private static void StandAlone_02_VerifySemanticModel(CSharpCompilation comp) var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); typeInfo = model.GetTypeInfo(tuple); - Assert.Equal("((var, var), var, System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c, System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); @@ -3273,7 +3273,7 @@ private static void StandAlone_05_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(System.Int32, System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 a, System.Int32 b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -3339,7 +3339,7 @@ private static void StandAlone_06_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(System.Int32, System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(System.Int32 a, System.Int32 b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -3785,7 +3785,7 @@ private static void StandAlone_14_VerifySemanticModel(CSharpCompilation comp, Lo Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -3839,7 +3839,7 @@ private static void StandAlone_14_VerifySemanticModel(CSharpCompilation comp, Lo Assert.Equal("((var (a,b), var c), int d)", tuples[0].ToString()); typeInfo = model.GetTypeInfo(tuples[0]); - Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[0]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[0]); @@ -3850,7 +3850,7 @@ private static void StandAlone_14_VerifySemanticModel(CSharpCompilation comp, Lo Assert.Equal("(var (a,b), var c)", tuples[1].ToString()); typeInfo = model.GetTypeInfo(tuples[1]); - Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[1]); @@ -3932,7 +3932,7 @@ private static void StandAlone_15_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("var (a,b)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("(var, var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(var a, var b)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -3986,7 +3986,7 @@ private static void StandAlone_15_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("((var (a,b), var c), int d)", tuples[0].ToString()); typeInfo = model.GetTypeInfo(tuples[0]); - Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[0]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[0]); @@ -3996,7 +3996,7 @@ private static void StandAlone_15_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("(var (a,b), var c)", tuples[1].ToString()); typeInfo = model.GetTypeInfo(tuples[1]); - Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuples[1]).IsIdentity); symbolInfo = model.GetSymbolInfo(tuples[1]); @@ -4258,7 +4258,7 @@ private static void StandAlone_18_VerifySemanticModel(CSharpCompilation comp, Lo Assert.Equal("var ((a,b), c)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -4291,7 +4291,7 @@ private static void StandAlone_18_VerifySemanticModel(CSharpCompilation comp, Lo var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); typeInfo = model.GetTypeInfo(tuple); - Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); @@ -4370,7 +4370,7 @@ private static void StandAlone_19_VerifySemanticModel(CSharpCompilation comp) Assert.Equal("var ((a,b), c)", declarations[0].ToString()); var typeInfo = model.GetTypeInfo(declarations[0]); - Assert.Equal("((var, var), var)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("((var a, var b), var c)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(declarations[0]).IsIdentity); var symbolInfo = model.GetSymbolInfo(declarations[0]); @@ -4403,7 +4403,7 @@ private static void StandAlone_19_VerifySemanticModel(CSharpCompilation comp) var tuple = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(); typeInfo = model.GetTypeInfo(tuple); - Assert.Equal("(((var, var), var), System.Int32)", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("(((var a, var b), var c), System.Int32 d)", typeInfo.Type.ToTestDisplayString()); Assert.Equal(typeInfo.Type, typeInfo.ConvertedType); Assert.True(model.GetConversion(tuple).IsIdentity); symbolInfo = model.GetSymbolInfo(tuple); diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb index 895efd8f99928..d27f5a3f899a4 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb @@ -322,8 +322,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim elementLocations = ArrayBuilder(Of Location).GetInstance(arguments.Count) ' prepare names - Dim names = ExtractTupleElementNames(arguments, diagnostics, - withInference:=Me.Compilation.LanguageVersion.InferTupleElementNames()) + Dim names = ExtractTupleElementNames(arguments, diagnostics) Dim elementNames = names.elementNames Dim inferredPositions = names.inferredPositions Dim hasErrors = names.hasErrors @@ -365,15 +364,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim inferredType As TupleTypeSymbol = Nothing If hasInferredType Then - inferredType = TupleTypeSymbol.Create(node.GetLocation, elements, locations, elementNames, Me.Compilation, shouldCheckConstraints:=True, syntax:=node, diagnostics:=diagnostics) + Dim disallowInferredNames = Me.Compilation.LanguageVersion.DisallowInferredTupleElementNames() + + inferredType = TupleTypeSymbol.Create(node.GetLocation, elements, locations, elementNames, Me.Compilation, + shouldCheckConstraints:=True, + errorPositions:=If(disallowInferredNames, inferredPositions, Nothing), + syntax:=node, diagnostics:=diagnostics) End If Dim tupleTypeOpt As NamedTypeSymbol = If(hasNaturalType, inferredType, Nothing) + '' Always track the inferred positions in the bound node, so that conversions don't produce a warning + '' for "dropped names" when the name was inferred. Return New BoundTupleLiteral(node, inferredType, elementNames, inferredPositions, boundArguments.ToImmutableAndFree(), tupleTypeOpt, hasErrors) End Function - Private Shared Function ExtractTupleElementNames(arguments As SeparatedSyntaxList(Of SimpleArgumentSyntax), diagnostics As DiagnosticBag, withInference As Boolean) _ + Private Shared Function ExtractTupleElementNames(arguments As SeparatedSyntaxList(Of SimpleArgumentSyntax), diagnostics As DiagnosticBag) _ As (elementNames As ImmutableArray(Of String), inferredPositions As ImmutableArray(Of Boolean), hasErrors As Boolean) Dim hasErrors = False @@ -400,9 +406,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic hasErrors = True End If Else - If withInference Then - inferredName = InferTupleElementName(argumentSyntax.Expression) - End If + inferredName = InferTupleElementName(argumentSyntax.Expression) End If CollectTupleFieldMemberName(name, i, numElements, elementNames) @@ -2904,13 +2908,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ElseIf left.HasErrors Then Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType) - ElseIf left.Type.IsTupleType AndAlso Not Compilation.LanguageVersion.InferTupleElementNames() AndAlso - IsMissingMemberAnInferredTupleName(left, rightName) Then - - Dim errorDiag = ErrorFactory.ErrorInfo(ERRID.ERR_TupleInferredNamesNotAvailable, type, rightName, - New VisualBasicRequiredLanguageVersion(LanguageVersion.VisualBasic15_3)) - Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, errorDiag, left) - Else If type.IsInterfaceType() Then ' In case IsExtensibleInterfaceNoUseSiteDiagnostics above failed because there were bad inherited interfaces. @@ -2926,41 +2923,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Try End Function - ''' - ''' When reporting a missing member error on a tuple type, determine whether that member - ''' could be an element with an inferred name (had the language version been above 15.3). - ''' - Private Shared Function IsMissingMemberAnInferredTupleName(left As BoundExpression, rightName As String) As Boolean - Debug.Assert(left.Type.IsTupleType) - - Dim declarations = left.Type.DeclaringSyntaxReferences - If declarations.Length <> 1 Then - Return False - End If - - Dim Syntax = declarations(0).GetSyntax() - If Syntax.Kind() <> SyntaxKind.TupleExpression Then - Return False - End If - - Dim arguments = DirectCast(Syntax, TupleExpressionSyntax).Arguments - Dim names = ExtractTupleElementNames(arguments, diagnostics:=Nothing, withInference:=True) - Dim elementNames = names.elementNames - Dim inferredPositions = names.inferredPositions - - If elementNames.IsDefault OrElse inferredPositions.IsDefault Then - Return False - End If - - For i = 0 To arguments.Count - 1 - If IdentifierComparison.Comparer.Equals(elementNames(i), rightName) AndAlso inferredPositions(i) Then - Return True - End If - Next - - Return False - End Function - ''' ''' Returns a bound node for left part of member access node with omitted left syntax. ''' In particular it handles member access inside With statement. diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb index 5c17fc8d4a345..ecdfb9c11b895 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb @@ -677,8 +677,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If(elementNames Is Nothing, Nothing, elementNames.ToImmutableAndFree()), binder.Compilation, binder.ShouldCheckConstraints, - syntax, - diagnostics) + errorPositions:=Nothing, + syntax:=syntax, + diagnostics:=diagnostics) End Function Private Shared Sub AnalyzeLookupResultForIllegalBaseTypeReferences(lookupResult As LookupResult, diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 40154db79f08f..3e3947ff04456 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2606,7 +2606,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic elementTypes:=typesBuilder.ToImmutableAndFree(), elementLocations:=elementLocations, elementNames:=elementNames, compilation:=Me, - shouldCheckConstraints:=False) + shouldCheckConstraints:=False, errorPositions:=Nothing) End Function Protected Overrides Function CommonCreateTupleTypeSymbol( @@ -2627,7 +2627,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic locationOpt:=Nothing, tupleCompatibleType:=underlyingType.EnsureVbSymbolOrNothing(Of NamedTypeSymbol)(NameOf(underlyingType)), elementLocations:=elementLocations, - elementNames:=elementNames) + elementNames:=elementNames, + errorPositions:=Nothing) End Function Protected Overrides Function CommonCreatePointerTypeSymbol(elementType As ITypeSymbol) As IPointerTypeSymbol diff --git a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb index 7ed87eb37e3ca..29b7a5f00cbd7 100644 --- a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb +++ b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb @@ -1,6 +1,7 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Namespace Microsoft.CodeAnalysis.VisualBasic ''' @@ -147,8 +148,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' Inference of tuple element names was added in VB 15.3 - Friend Function InferTupleElementNames(self As LanguageVersion) As Boolean - Return self >= LanguageVersion.VisualBasic15_3 + Friend Function DisallowInferredTupleElementNames(self As LanguageVersion) As Boolean + Return self < Feature.InferredTupleNames.GetLanguageVersion() End Function End Module diff --git a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb index 079f146b83976..c744d0b1241b7 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb @@ -33,6 +33,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax BinaryLiterals Tuples IOperation + InferredTupleNames End Enum Friend Module FeatureExtensions @@ -86,6 +87,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Feature.DigitSeparators Return LanguageVersion.VisualBasic15 + Case Feature.InferredTupleNames + Return LanguageVersion.VisualBasic15_3 + Case Else Throw ExceptionUtilities.UnexpectedValue(feature) End Select diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 5e8aa665338ea..bc371dc8d2a29 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -3,7 +3,7 @@ Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.MapSpecifiedToEffectiveVersion(version As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.ToDisplayString(version As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> String Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.TryParse(version As String, ByRef result As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> Boolean -Microsoft.CodeAnalysis.VisualBasic.SyntaxExtensions.TryGetInferredMemberName(expression As Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax) -> String +Microsoft.CodeAnalysis.VisualBasic.SyntaxExtensions.TryGetInferredMemberName(syntax As Microsoft.CodeAnalysis.SyntaxNode) -> String Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.ConflictMarkerTrivia = 792 -> Microsoft.CodeAnalysis.VisualBasic.SyntaxKind Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ConflictMarkerTrivia(text As String) -> Microsoft.CodeAnalysis.SyntaxTrivia Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsReservedTupleElementName(elementName As String) -> Boolean \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleFieldSymbol.vb index ca700628d1325..ccda2402c7b68 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleFieldSymbol.vb @@ -210,10 +210,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Inherits TupleElementFieldSymbol Private _name As String + Private _cannotUse As Boolean ' With LanguageVersion 15, we will produce named elements that should not be used Public Sub New(container As TupleTypeSymbol, underlyingField As FieldSymbol, name As String, + cannotUse As Boolean, tupleElementOrdinal As Integer, location As Location, isImplicitlyDeclared As Boolean, @@ -226,8 +228,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols "fields that map directly to underlying should not be represented by " + NameOf(TupleVirtualElementFieldSymbol)) Me._name = name + Me._cannotUse = cannotUse End Sub + Friend Overrides Function GetUseSiteErrorInfo() As DiagnosticInfo + If _cannotUse Then + Return ErrorFactory.ErrorInfo(ERRID.ERR_TupleInferredNamesNotAvailable, _name, + New VisualBasicRequiredLanguageVersion(LanguageVersion.VisualBasic15_3)) + End If + + Return MyBase.GetUseSiteErrorInfo() + End Function + Public Overrides ReadOnly Property Name As String Get Return Me._name diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleTypeSymbol.vb index 0503727199f67..6c848a15fc313 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Tuples/TupleTypeSymbol.vb @@ -20,6 +20,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Private ReadOnly _providedElementNames As ImmutableArray(Of String) + ''' + ''' Which element names were inferred and therefore cannot be used. + ''' If none of the element names were inferred, or inferred names can be used (no tracking necessary), leave as default. + ''' This information is ignored in type equality and comparison. + ''' + Private ReadOnly _errorPositions As ImmutableArray(Of Boolean) + ''' ''' Actual element names. ''' Could be different from _providedElementNames because of case insensitivity. @@ -317,11 +324,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Private Sub New(locationOpt As Location, underlyingType As NamedTypeSymbol, elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String), elementTypes As ImmutableArray(Of TypeSymbol)) - Me.New(If((locationOpt Is Nothing), ImmutableArray(Of Location).Empty, ImmutableArray.Create(Of Location)(locationOpt)), underlyingType, elementLocations, elementNames, elementTypes) + Private Sub New(locationOpt As Location, underlyingType As NamedTypeSymbol, elementLocations As ImmutableArray(Of Location), + elementNames As ImmutableArray(Of String), elementTypes As ImmutableArray(Of TypeSymbol), + errorPositions As ImmutableArray(Of Boolean)) + + Me.New(If((locationOpt Is Nothing), ImmutableArray(Of Location).Empty, ImmutableArray.Create(Of Location)(locationOpt)), + underlyingType, elementLocations, elementNames, elementTypes, errorPositions) End Sub - Private Sub New(locations As ImmutableArray(Of Location), underlyingType As NamedTypeSymbol, elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String), elementTypes As ImmutableArray(Of TypeSymbol)) + Private Sub New(locations As ImmutableArray(Of Location), underlyingType As NamedTypeSymbol, + elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String), + elementTypes As ImmutableArray(Of TypeSymbol), errorPositions As ImmutableArray(Of Boolean)) + MyBase.New(underlyingType) Debug.Assert(elementLocations.IsDefault OrElse elementLocations.Length = elementTypes.Length) Debug.Assert(elementNames.IsDefault OrElse elementNames.Length = elementTypes.Length) @@ -330,6 +344,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Me._providedElementNames = elementNames Me._elementTypes = elementTypes Me._locations = locations + Me._errorPositions = errorPositions End Sub Friend Shared Function Create( @@ -339,6 +354,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols elementNames As ImmutableArray(Of String), compilation As VisualBasicCompilation, shouldCheckConstraints As Boolean, + errorPositions As ImmutableArray(Of Boolean), Optional syntax As SyntaxNode = Nothing, Optional diagnostics As DiagnosticBag = Nothing) As TupleTypeSymbol Debug.Assert(Not shouldCheckConstraints OrElse syntax IsNot Nothing) @@ -355,7 +371,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Emit.NoPia.EmbeddedTypesManager.IsValidEmbeddableType(tupleUnderlyingType, syntax, diagnostics) End If - Dim constructedType = TupleTypeSymbol.Create(locationOpt, tupleUnderlyingType, elementLocations, elementNames) + Dim constructedType = TupleTypeSymbol.Create(locationOpt, tupleUnderlyingType, elementLocations, elementNames, errorPositions) If shouldCheckConstraints Then constructedType.CheckConstraints(syntax, elementLocations, diagnostics) End If @@ -364,18 +380,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Shared Function Create(tupleCompatibleType As NamedTypeSymbol) As TupleTypeSymbol - Return TupleTypeSymbol.Create(ImmutableArray(Of Location).Empty, tupleCompatibleType, Nothing, Nothing) + Return TupleTypeSymbol.Create(ImmutableArray(Of Location).Empty, tupleCompatibleType, Nothing, Nothing, Nothing) End Function Public Shared Function Create(tupleCompatibleType As NamedTypeSymbol, elementNames As ImmutableArray(Of String)) As TupleTypeSymbol - Return TupleTypeSymbol.Create(ImmutableArray(Of Location).Empty, tupleCompatibleType, Nothing, elementNames) + Return TupleTypeSymbol.Create(ImmutableArray(Of Location).Empty, tupleCompatibleType, Nothing, elementNames, errorPositions:=Nothing) End Function - Public Shared Function Create(locationOpt As Location, tupleCompatibleType As NamedTypeSymbol, elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String)) As TupleTypeSymbol - Return TupleTypeSymbol.Create(If((locationOpt Is Nothing), ImmutableArray(Of Location).Empty, ImmutableArray.Create(Of Location)(locationOpt)), tupleCompatibleType, elementLocations, elementNames) + Public Shared Function Create(locationOpt As Location, tupleCompatibleType As NamedTypeSymbol, + elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String), + errorPositions As ImmutableArray(Of Boolean)) As TupleTypeSymbol + + Return TupleTypeSymbol.Create(If((locationOpt Is Nothing), ImmutableArray(Of Location).Empty, ImmutableArray.Create(Of Location)(locationOpt)), + tupleCompatibleType, elementLocations, elementNames, errorPositions) End Function - Public Shared Function Create(locations As ImmutableArray(Of Location), tupleCompatibleType As NamedTypeSymbol, elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String)) As TupleTypeSymbol + Public Shared Function Create(locations As ImmutableArray(Of Location), tupleCompatibleType As NamedTypeSymbol, + elementLocations As ImmutableArray(Of Location), elementNames As ImmutableArray(Of String), + errorPositions As ImmutableArray(Of Boolean)) As TupleTypeSymbol + Debug.Assert(tupleCompatibleType.IsTupleCompatible()) Dim elementTypes As ImmutableArray(Of TypeSymbol) @@ -390,7 +413,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols elementTypes = tupleCompatibleType.TypeArgumentsNoUseSiteDiagnostics End If - Return New TupleTypeSymbol(locations, tupleCompatibleType, elementLocations, elementNames, elementTypes) + Return New TupleTypeSymbol(locations, tupleCompatibleType, elementLocations, elementNames, elementTypes, errorPositions) End Function Private Shared Function EnsureRestExtensionsAreTuples(tupleCompatibleType As NamedTypeSymbol) As NamedTypeSymbol @@ -412,7 +435,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim typeArgumentsBuilder As ArrayBuilder(Of TypeWithModifiers) = ArrayBuilder(Of TypeWithModifiers).GetInstance(TupleTypeSymbol.RestPosition) Do - Dim extensionTuple As TupleTypeSymbol = TupleTypeSymbol.Create(CType(Nothing, Location), tupleCompatibleType, Nothing, Nothing) + Dim extensionTuple As TupleTypeSymbol = TupleTypeSymbol.Create(CType(Nothing, Location), tupleCompatibleType, Nothing, Nothing, Nothing) tupleCompatibleType = nonTupleTypeChain.Pop() tupleCompatibleType = TupleTypeSymbol.ReplaceRestExtensionType(tupleCompatibleType, typeArgumentsBuilder, extensionTuple) Loop While nonTupleTypeChain.Count <> 0 @@ -448,10 +471,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Function WithUnderlyingType(newUnderlyingType As NamedTypeSymbol) As TupleTypeSymbol Debug.Assert(Not newUnderlyingType.IsTupleType AndAlso newUnderlyingType.IsTupleOrCompatibleWithTupleOfCardinality(Me._elementTypes.Length)) - Return TupleTypeSymbol.Create(Me._locations, newUnderlyingType, Me._elementLocations, Me._providedElementNames) + Return TupleTypeSymbol.Create(Me._locations, newUnderlyingType, Me._elementLocations, Me._providedElementNames, Me._errorPositions) End Function Friend Function WithElementNames(newElementNames As ImmutableArray(Of String)) As TupleTypeSymbol + Debug.Assert(newElementNames.IsDefault OrElse Me._elementTypes.Length = newElementNames.Length) If Me._providedElementNames.IsDefault Then @@ -464,7 +488,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End If - Return New TupleTypeSymbol(CType(Nothing, Location), Me._underlyingType, Nothing, newElementNames, Me._elementTypes) + ' Note: this method is used to preserved names during target-typing, it doesn't need to preserved error positions + Return New TupleTypeSymbol(CType(Nothing, Location), Me._underlyingType, Nothing, newElementNames, Me._elementTypes, Nothing) End Function Friend Shared Sub GetUnderlyingTypeChain(underlyingTupleType As NamedTypeSymbol, underlyingTupleTypeChain As ArrayBuilder(Of NamedTypeSymbol)) @@ -712,9 +737,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols defaultTupleField = New TupleVirtualElementFieldSymbol(Me, FieldSymbol, defaultName, - tupleFieldIndex, - location, - defaultImplicitlyDeclared, + cannotUse:=False, + tupleElementOrdinal:=tupleFieldIndex, + location:=location, + isImplicitlyDeclared:=defaultImplicitlyDeclared, correspondingDefaultFieldOpt:=Nothing) Else Debug.Assert(IdentifierComparison.Equals(FieldSymbol.Name, defaultName), "top level underlying field must match default name") @@ -731,11 +757,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols members.Add(defaultTupleField) If defaultImplicitlyDeclared AndAlso Not String.IsNullOrEmpty(providedName) Then + Dim isError = If(_errorPositions.IsDefault, False, _errorPositions(tupleFieldIndex)) + ' The name given doesn't match the default name Item8, etc. ' Add a virtual field with the given name members.Add(New TupleVirtualElementFieldSymbol(Me, FieldSymbol, providedName, + isError, tupleFieldIndex, location, isImplicitlyDeclared:=False, @@ -1028,7 +1057,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Overrides Function InternalSubstituteTypeParameters(substitution As TypeSubstitution) As TypeWithModifiers Dim substitutedUnderlying = DirectCast(Me.TupleUnderlyingType.InternalSubstituteTypeParameters(substitution).Type, NamedTypeSymbol) - Dim tupleType = TupleTypeSymbol.Create(Me._locations, substitutedUnderlying, Me._elementLocations, Me._providedElementNames) + Dim tupleType = TupleTypeSymbol.Create(Me._locations, substitutedUnderlying, Me._elementLocations, + Me._providedElementNames, Me._errorPositions) Return New TypeWithModifiers(tupleType, Nothing) End Function diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxExtensions.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxExtensions.vb index 342d1c9328526..74fed361d198a 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxExtensions.vb @@ -109,13 +109,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' Returns Nothing if unsuccessful ''' - Public Function TryGetInferredMemberName(expression As ExpressionSyntax) As String - If expression Is Nothing Then + Public Function TryGetInferredMemberName(syntax As SyntaxNode) As String + If syntax Is Nothing Then + Return Nothing + End If + + Dim expr = TryCast(syntax, ExpressionSyntax) + If expr Is Nothing Then Return Nothing End If Dim ignore As XmlNameSyntax = Nothing - Dim nameToken As SyntaxToken = expression.ExtractAnonymousTypeMemberName(ignore) + Dim nameToken As SyntaxToken = expr.ExtractAnonymousTypeMemberName(ignore) Return If(nameToken.Kind() = SyntaxKind.IdentifierToken, nameToken.ValueText, Nothing) End Function End Module diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index 450d62e96306c..f3cc8d7983f6d 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -10578,7 +10578,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property ''' - ''' Looks up a localized string similar to Tuple type '{0}' does not have an explicitly named element '{1}'. Please use language version {2} or greater to access a unnamed element by its inferred name.. + ''' Looks up a localized string similar to Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name.. ''' Friend ReadOnly Property ERR_TupleInferredNamesNotAvailable() As String Get diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 79264551cbc30..a4ac68110d639 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5477,7 +5477,7 @@ Provided source code kind is unsupported or invalid: '{0}' - Tuple type '{0}' does not have an explicitly named element '{1}'. Please use language version {2} or greater to access a unnamed element by its inferred name. + Tuple element name '{0}' is inferred. Please use language version {1} or greater to access an element by its inferred name. '{0}' is for evaluation purposes only and is subject to change or removal in future updates. diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index 73157aa211e78..05b2da99f587c 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -5885,11 +5885,11 @@ End Class Dim nodes = tree.GetCompilationUnitRoot().DescendantNodes() Dim yTuple = nodes.OfType(Of TupleExpressionSyntax)().ElementAt(0) - Assert.Equal("(System.Int32, System.Int32, b As System.Int32, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32, System.Int32)", + Assert.Equal("(a As System.Int32, System.Int32, b As System.Int32, System.Int32, System.Int32, e As System.Int32, f As System.Int32, System.Int32, System.Int32, System.Int32)", model.GetTypeInfo(yTuple).Type.ToTestDisplayString()) Dim zTuple = nodes.OfType(Of TupleExpressionSyntax)().ElementAt(1) - Assert.Equal("(x As System.Int32, System.Int32)", model.GetTypeInfo(zTuple).Type.ToTestDisplayString()) + Assert.Equal("(x As System.Int32, b As System.Int32)", model.GetTypeInfo(zTuple).Type.ToTestDisplayString()) End Sub) verifier.VerifyDiagnostics() @@ -5994,7 +5994,140 @@ End Class parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15)) comp.AssertTheseDiagnostics( -BC37289: Tuple type '(Integer, Integer)' does not have an explicitly named element 'A'. Please use language version 15.3 or greater to access a unnamed element by its inferred name. +BC37289: Tuple element name 'a' is inferred. Please use language version 15.3 or greater to access an element by its inferred name. + System.Console.Write(t.A) + ~~~ +BC30456: 'a' is not a member of '(Integer, Integer)'. + System.Console.Write(GetTuple().a) + ~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UseSiteDiagnosticOnTupleField() + Dim missingComp = CreateCompilationWithMscorlibAndVBRuntime( + + +Public Class Missing +End Class + +) + missingComp.VerifyDiagnostics() + + Dim libComp = CreateCompilationWithMscorlibAndVBRuntime( + + +Public Class C + Public Shared Function GetTuple() As (Missing, Integer) + Throw New System.Exception() + End Function +End Class + +, additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, missingComp.ToMetadataReference()}) + libComp.VerifyDiagnostics() + + Dim source = + + +Class D + Sub M() + System.Console.Write(C.GetTuple().Item1) + End Sub +End Class + + + + Dim comp15 = CreateCompilationWithMscorlibAndVBRuntime(source, + additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, libComp.ToMetadataReference()}, + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15)) + + comp15.AssertTheseDiagnostics( +BC30652: Reference required to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' containing the type 'Missing'. Add one to your project. + System.Console.Write(C.GetTuple().Item1) + ~~~~~~~~~~~~ + ) + + Dim comp15_3 = CreateCompilationWithMscorlibAndVBRuntime(source, + additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, libComp.ToMetadataReference()}, + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15_3)) + + comp15_3.AssertTheseDiagnostics( +BC30652: Reference required to assembly 'UseSiteDiagnosticOnTupleField_missingComp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' containing the type 'Missing'. Add one to your project. + System.Console.Write(C.GetTuple().Item1) + ~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UseSiteDiagnosticOnTupleField2() + Dim source = + + +Class C + Sub M() + Dim a = 1 + Dim t = (a, 2) + System.Console.Write(t.a) + End Sub +End Class +Namespace System + Public Structure ValueTuple(Of T1, T2) + Public Sub New(item1 As T1, item2 As T2) + End Sub + End Structure +End Namespace + + + + Dim comp15 = CreateCompilationWithMscorlibAndVBRuntime(source, + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15)) + + comp15.AssertTheseDiagnostics( +BC35000: Requested operation is not available because the runtime library function 'ValueTuple.Item1' is not defined. + System.Console.Write(t.a) + ~~~ + ) + + Dim comp15_3 = CreateCompilationWithMscorlibAndVBRuntime(source, + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15_3)) + + comp15_3.AssertTheseDiagnostics( +BC35000: Requested operation is not available because the runtime library function 'ValueTuple.Item1' is not defined. + System.Console.Write(t.a) + ~~~ + ) + End Sub + + + Public Sub MissingMemberAccessWithExtensionWithVB15() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +Imports System +Class C + Sub M() + Dim a As Integer = 1 + Dim t = (a, 2) + System.Console.Write(t.A) + System.Console.Write(GetTuple().a) + End Sub + Function GetTuple() As (Integer, Integer) + Return (1, 2) + End Function +End Class +Module Extensions + <System.Runtime.CompilerServices.Extension()> + Public Function A(self As (Integer, Action)) As String + Return Nothing + End Function +End Module + +, + additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef}, + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15)) + + comp.AssertTheseDiagnostics( +BC37289: Tuple element name 'a' is inferred. Please use language version 15.3 or greater to access an element by its inferred name. System.Console.Write(t.A) ~~~ BC30456: 'a' is not a member of '(Integer, Integer)'. @@ -6086,7 +6219,7 @@ End Class End Sub - Public Sub InferredNames_ExtensionInvokedInVB15ButNotVB15_3() + Public Sub InferredNames_ExtensionNowFailsInVB15ButNotVB15_3() Dim source = Imports System @@ -6105,11 +6238,16 @@ Module Extensions End Module - Dim verifier15 = CompileAndVerify(source, + ' When VB 15 shipped, no tuple element would be found/inferred, so the extension method was called. + ' The VB 15.3 compiler disallows that, even when LanguageVersion is 15. + Dim comp15 = CreateCompilationWithMscorlibAndVBRuntime(source, additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef}, - parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15), - expectedOutput:="extension") - verifier15.VerifyDiagnostics() + parseOptions:=TestOptions.Regular.WithLanguageVersion(LanguageVersion.VisualBasic15)) + comp15.AssertTheseDiagnostics( +BC37289: Tuple element name 'M' is inferred. Please use language version 15.3 or greater to access an element by its inferred name. + t.M() + ~~~ + ) Dim verifier15_3 = CompileAndVerify(source, additionalRefs:={ValueTupleRef, SystemRuntimeFacadeRef, SystemCoreRef}, diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs index 33e5042cb3dc6..1a58ad0c16fca 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs @@ -4242,6 +4242,7 @@ void M() } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)] + [WorkItem(19047, "https://github.com/dotnet/roslyn/issues/19047")] public async Task ExplicitTupleNameAdded_DeconstructionDeclaration() { var code = @" @@ -4261,13 +4262,16 @@ class C static int y = 1; void M() { - var t = ((C.y, (C.y, _)) = (1, (C.y, 3))); + var t = (((int)C.y, ((int)C.y, _)) = (1, (C.y, 3))); } }"; + // This refactoring should be blocked with an annotation, as the result of a cast is an L-value + // Follow-up issue: https://github.com/dotnet/roslyn/issues/19047 await TestInRegularAndScriptAsync(code, expected, ignoreTrivia: false); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)] + [WorkItem(19047, "https://github.com/dotnet/roslyn/issues/19047")] public async Task ExplicitTupleNameAdded_DeconstructionDeclaration2() { var code = @" @@ -4287,9 +4291,11 @@ class C static int y = 1; void M() { - var t = ((C.y, _) = (1, 2)); + var t = (((int)C.y, _) = (1, 2)); } }"; + // This refactoring should be blocked with an annotation, as the result of a cast is an L-value + // Follow-up issue: https://github.com/dotnet/roslyn/issues/19047 await TestInRegularAndScriptAsync(code, expected, ignoreTrivia: false); } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs index 6ab39853e2041..b4be75cb1764c 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs @@ -131,7 +131,23 @@ void M() var t = (1, b); System.Console.Write(t.[|b|]); } -}", +} + +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + + public ValueTuple(T1 item1, T2 item2) + { + this.Item1 = item1; + this.Item2 = item2; + } + } +} +", LanguageVersion.Latest, new CSharpParseOptions(LanguageVersion.CSharp7)); } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb index a2036a6230ac4..c5f2149e0ae18 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb @@ -617,7 +617,7 @@ End Namespace", System.Console.Write(t.a) End Sub - Private Shared Function GetT(a As Integer) As (Integer, b As Integer) + Private Shared Function GetT(a As Integer) As (a As Integer, b As Integer) Return (a, b:=2) End Function End Class diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs index 6d1f149a8ef45..ee753a4b9dee4 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/MissingAssemblyTests.cs @@ -801,7 +801,7 @@ static void M() // Code size 2 (0x2) .maxstack 1 .locals init (int V_0, //x - (int, int) V_1, //y + (int x, int) V_1, //y (int, int, (int, int)) V_2) //z IL_0000: ldloc.1 IL_0001: ret @@ -859,7 +859,7 @@ static void M() // Code size 2 (0x2) .maxstack 1 .locals init (int V_0, //x - (int, int) V_1, //y + (int x, int) V_1, //y (int, int, (int, int)) V_2) //z IL_0000: ldloc.0 IL_0001: ret diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb index b6099c7814d65..b221de74df3ed 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/MissingAssemblyTests.vb @@ -612,7 +612,7 @@ End Class" // Code size 2 (0x2) .maxstack 1 .locals init (Integer V_0, //x - (Integer, Integer) V_1, //y + (x As Integer, Integer) V_1, //y (Integer, Integer, (Integer, Integer)) V_2) //z IL_0000: ldloc.1 IL_0001: ret @@ -663,7 +663,7 @@ End Class" // Code size 2 (0x2) .maxstack 1 .locals init (Integer V_0, //x - (Integer, Integer) V_1, //y + (x As Integer, Integer) V_1, //y (Integer, Integer, (Integer, Integer)) V_2) //z IL_0000: ldloc.0 IL_0001: ret