未验证 提交 ca994907 编写于 作者: J Julien Couvreur 提交者: GitHub

Track and check names in tuple equality (#25396)

上级 2f893893
......@@ -207,7 +207,7 @@ private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOpe
(right.Type is null && right.IsLiteralDefault()))
{
Error(diagnostics, ErrorCode.ERR_AmbigBinaryOps, node, node.OperatorToken.Text, left.Display, right.Display);
return new TupleBinaryOperatorInfo.Multiple(ImmutableArray<TupleBinaryOperatorInfo>.Empty, left.Type, right.Type);
return TupleBinaryOperatorInfo.Multiple.ErrorInstance;
}
// Aside from default (which we fixed or ruled out above) and tuple literals,
......@@ -221,11 +221,12 @@ private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOpe
if (leftCardinality != rightCardinality)
{
Error(diagnostics, ErrorCode.ERR_TupleSizesMismatchForBinOps, node, leftCardinality, rightCardinality);
return new TupleBinaryOperatorInfo.Multiple(ImmutableArray<TupleBinaryOperatorInfo>.Empty, leftConvertedTypeOpt: null, rightConvertedTypeOpt: null);
return TupleBinaryOperatorInfo.Multiple.ErrorInstance;
}
ImmutableArray<BoundExpression> leftParts = GetTupleArgumentsOrPlaceholders(left);
ImmutableArray<BoundExpression> rightParts = GetTupleArgumentsOrPlaceholders(right);
(ImmutableArray<BoundExpression> leftParts, ImmutableArray<string> leftNames) = GetTupleArgumentsOrPlaceholders(left);
(ImmutableArray<BoundExpression> rightParts, ImmutableArray<string> rightNames) = GetTupleArgumentsOrPlaceholders(right);
ReportNamesMismatchesIfAny(left, right, leftNames, rightNames, diagnostics);
int length = leftParts.Length;
Debug.Assert(length == rightParts.Length);
......@@ -245,12 +246,80 @@ private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOpe
bool rightNullable = right.Type?.IsNullableType() == true;
bool isNullable = leftNullable || rightNullable;
TypeSymbol leftTupleType = MakeConvertedType(operators.SelectAsArray(o => o.LeftConvertedTypeOpt), node.Left, leftParts, isNullable, compilation, diagnostics);
TypeSymbol rightTupleType = MakeConvertedType(operators.SelectAsArray(o => o.RightConvertedTypeOpt), node.Right, rightParts, isNullable, compilation, diagnostics);
TypeSymbol leftTupleType = MakeConvertedType(operators.SelectAsArray(o => o.LeftConvertedTypeOpt), node.Left, leftParts, leftNames, isNullable, compilation, diagnostics);
TypeSymbol rightTupleType = MakeConvertedType(operators.SelectAsArray(o => o.RightConvertedTypeOpt), node.Right, rightParts, rightNames, isNullable, compilation, diagnostics);
return new TupleBinaryOperatorInfo.Multiple(operators, leftTupleType, rightTupleType);
}
/// <summary>
/// If an element in a tuple literal has an explicit name which doesn't match the name on the other side, we'll warn.
/// The user can either remove the name, or fix it.
///
/// This method handles two expressions, each of which is either a tuple literal or an expression with tuple type.
/// In a tuple literal, each element can have an explicit name, an inferred name or no name.
/// In an expression of tuple type, each element can have a name or not.
/// </summary>
private static void ReportNamesMismatchesIfAny(BoundExpression left, BoundExpression right,
ImmutableArray<string> leftNames, ImmutableArray<string> rightNames, DiagnosticBag diagnostics)
{
bool leftIsTupleLiteral = left.Kind == BoundKind.TupleLiteral;
bool rightIsTupleLiteral = right.Kind == BoundKind.TupleLiteral;
if (!leftIsTupleLiteral && !rightIsTupleLiteral)
{
return;
}
bool leftNoNames = leftNames.IsDefault;
bool rightNoNames = rightNames.IsDefault;
if (leftNoNames && rightNoNames)
{
return;
}
Debug.Assert(leftNoNames || rightNoNames || leftNames.Length == rightNames.Length);
ImmutableArray<bool> leftInferred = leftIsTupleLiteral ? ((BoundTupleLiteral)left).InferredNamesOpt : default;
bool leftNoInferredNames = leftInferred.IsDefault;
ImmutableArray<bool> rightInferred = rightIsTupleLiteral ? ((BoundTupleLiteral)right).InferredNamesOpt : default;
bool rightNoInferredNames = rightInferred.IsDefault;
int length = leftNoNames ? rightNames.Length : leftNames.Length;
for (int i = 0; i < length; i++)
{
string leftName = leftNoNames ? null : leftNames[i];
string rightName = rightNoNames ? null : rightNames[i];
bool different = string.CompareOrdinal(rightName, leftName) != 0;
if (!different)
{
continue;
}
bool leftWasInferred = leftNoInferredNames ? false : leftInferred[i];
bool rightWasInferred = rightNoInferredNames ? false : rightInferred[i];
bool leftComplaint = leftIsTupleLiteral && leftName != null && !leftWasInferred;
bool rightComplaint = rightIsTupleLiteral && rightName != null && !rightWasInferred;
if (!leftComplaint && !rightComplaint)
{
// No complaints, let's move on
continue;
}
// When in doubt, we'll complain on the right side if it's a literal
bool useRight = (leftComplaint && rightComplaint) ? rightIsTupleLiteral : rightComplaint;
Location location = ((BoundTupleLiteral)(useRight ? right : left)).Arguments[i].Syntax.Parent.Location;
string complaintName = useRight ? rightName : leftName;
diagnostics.Add(ErrorCode.WRN_TupleBinopLiteralNameMismatch, location, complaintName);
}
}
internal static BoundExpression GiveTupleTypeToDefaultLiteralIfNeeded(BoundExpression expr, TypeSymbol targetType)
{
if (!expr.IsLiteralDefault() || targetType is null)
......@@ -298,16 +367,25 @@ private static int GetTupleCardinality(BoundExpression expr)
return -1;
}
private static ImmutableArray<BoundExpression> GetTupleArgumentsOrPlaceholders(BoundExpression expr)
/// <summary>
/// Given a tuple literal or expression, we'll get two arrays:
/// - the elements from the literal, or some placeholder with proper type (for tuple expressions)
/// - the elements' names
/// </summary>
private static (ImmutableArray<BoundExpression> Elements, ImmutableArray<string> Names) GetTupleArgumentsOrPlaceholders(BoundExpression expr)
{
if (expr.Kind == BoundKind.TupleLiteral)
{
return ((BoundTupleLiteral)expr).Arguments;
var tuple = (BoundTupleLiteral)expr;
return (tuple.Arguments, tuple.ArgumentNamesOpt);
}
// placeholder bound nodes with the proper types are sufficient to bind the element-wise binary operators
return expr.Type.StrippedType().TupleElementTypes
TypeSymbol tupleType = expr.Type.StrippedType();
ImmutableArray<BoundExpression> placeholders = tupleType.TupleElementTypes
.SelectAsArray((t, s) => (BoundExpression)new BoundTupleOperandPlaceholder(s, t), expr.Syntax);
return (placeholders, tupleType.TupleElementNames);
}
/// <summary>
......@@ -316,7 +394,8 @@ private static ImmutableArray<BoundExpression> GetTupleArgumentsOrPlaceholders(B
/// If any of the elements is typeless, then the tuple is typeless too.
/// </summary>
private TypeSymbol MakeConvertedType(ImmutableArray<TypeSymbol> convertedTypes, CSharpSyntaxNode syntax,
ImmutableArray<BoundExpression> elements, bool isNullable, CSharpCompilation compilation, DiagnosticBag diagnostics)
ImmutableArray<BoundExpression> elements, ImmutableArray<string> names,
bool isNullable, CSharpCompilation compilation, DiagnosticBag diagnostics)
{
foreach (var convertedType in convertedTypes)
{
......@@ -330,7 +409,7 @@ private static ImmutableArray<BoundExpression> GetTupleArgumentsOrPlaceholders(B
// PROTOTYPE(tuple-equality) Add test for violated tuple constraint
var tuple = TupleTypeSymbol.Create(locationOpt: null, elementTypes: convertedTypes,
elementLocations, elementNames: default, compilation,
elementLocations, elementNames: names, compilation,
shouldCheckConstraints: true, errorPositions: default, syntax, diagnostics);
if (!isNullable)
......
......@@ -94,6 +94,9 @@ internal class Multiple : TupleBinaryOperatorInfo
{
internal readonly ImmutableArray<TupleBinaryOperatorInfo> Operators;
static internal readonly Multiple ErrorInstance =
new Multiple(operators: ImmutableArray<TupleBinaryOperatorInfo>.Empty, leftConvertedTypeOpt: null, rightConvertedTypeOpt: null);
internal Multiple(ImmutableArray<TupleBinaryOperatorInfo> operators, TypeSymbol leftConvertedTypeOpt, TypeSymbol rightConvertedTypeOpt)
: base(leftConvertedTypeOpt, rightConvertedTypeOpt)
{
......
......@@ -14299,6 +14299,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to The tuple element name &apos;{0}&apos; is ignored because a different name or no name is specified on the other side of the tuple == or != operator..
/// </summary>
internal static string WRN_TupleBinopLiteralNameMismatch {
get {
return ResourceManager.GetString("WRN_TupleBinopLiteralNameMismatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator..
/// </summary>
internal static string WRN_TupleBinopLiteralNameMismatch_Title {
get {
return ResourceManager.GetString("WRN_TupleBinopLiteralNameMismatch_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The tuple element name &apos;{0}&apos; is ignored because a different name or no name is specified by the target type &apos;{1}&apos;..
/// </summary>
......
......@@ -5034,6 +5034,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_TupleLiteralNameMismatch_Title" xml:space="preserve">
<value>The tuple element name is ignored because a different name or no name is specified by the assignment target.</value>
</data>
<data name="WRN_TupleBinopLiteralNameMismatch" xml:space="preserve">
<value>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</value>
</data>
<data name="WRN_TupleBinopLiteralNameMismatch_Title" xml:space="preserve">
<value>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</value>
</data>
<data name="ERR_PredefinedValueTupleTypeMustBeStruct" xml:space="preserve">
<value>Predefined type '{0}' must be a struct.</value>
</data>
......
......@@ -1562,6 +1562,7 @@ internal enum ErrorCode
ERR_TupleSizesMismatchForBinOps = 8373,
ERR_ExpressionTreeContainsTupleBinOp = 8374,
WRN_TupleBinopLiteralNameMismatch = 8375,
#endregion diagnostics introduced for C# 7.3
// Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd)
......
......@@ -322,6 +322,7 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_TupleLiteralNameMismatch:
case ErrorCode.WRN_Experimental:
case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable:
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
return 1;
default:
return 0;
......
......@@ -178,6 +178,7 @@ public static bool IsWarning(ErrorCode code)
case ErrorCode.WRN_FilterIsConstantFalse:
case ErrorCode.WRN_FilterIsConstantFalseRedundantTryCatch:
case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable:
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
return true;
default:
return false;
......
......@@ -8635,6 +8635,16 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -8635,6 +8635,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<target state="new">An expression tree may not contain a tuple == or != operator</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch">
<source>The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name '{0}' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
<trans-unit id="WRN_TupleBinopLiteralNameMismatch_Title">
<source>The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</source>
<target state="new">The tuple element name is ignored because a different name or no name is specified on the other side of the tuple == or != operator.</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -749,7 +749,7 @@ static void Main()
Assert.Equal("(s, null)", tuple1.ToString());
var tupleType1 = model.GetTypeInfo(tuple1);
Assert.Null(tupleType1.Type);
Assert.Equal("(System.String, System.String)", tupleType1.ConvertedType.ToTestDisplayString());
Assert.Equal("(System.String s, System.String)", tupleType1.ConvertedType.ToTestDisplayString());
var tuple1Null = tuple1.Arguments[1].Expression;
var tuple1NullTypeInfo = model.GetTypeInfo(tuple1Null);
......@@ -762,7 +762,7 @@ static void Main()
Assert.Equal("(null, s)", tuple2.ToString());
var tupleType2 = model.GetTypeInfo(tuple2);
Assert.Null(tupleType2.Type);
Assert.Equal("(System.String, System.String)", tupleType2.ConvertedType.ToTestDisplayString());
Assert.Equal("(System.String, System.String s)", tupleType2.ConvertedType.ToTestDisplayString());
var tuple2Null = tuple2.Arguments[0].Expression;
var tuple2NullTypeInfo = model.GetTypeInfo(tuple2Null);
......@@ -866,7 +866,7 @@ static void Main()
Assert.Equal("(1L, t2)", tuple.ToString());
var tupleType = model.GetTypeInfo(tuple);
Assert.Equal("(System.Int64, (System.Int32, System.String) t2)", tupleType.Type.ToTestDisplayString());
Assert.Equal("(System.Int64, (System.Int64, System.String))", tupleType.ConvertedType.ToTestDisplayString());
Assert.Equal("(System.Int64, (System.Int64, System.String) t2)", tupleType.ConvertedType.ToTestDisplayString());
var t2 = tuple.Arguments[1].Expression;
Assert.Equal("t2", t2.ToString());
......@@ -1368,7 +1368,7 @@ static void Main()
var tupleType = model.GetTypeInfo(tuple);
Assert.Null(tupleType.Type);
Assert.Equal("((System.String, System.String), (System.String, System.String))",
Assert.Equal("((System.String, System.String), (System.String, System.String) t)",
tupleType.ConvertedType.ToTestDisplayString());
// ... its t ...
......@@ -1736,13 +1736,13 @@ public static void Main()
Assert.Equal("(d1, null)", tuple1.ToString());
var tupleType1 = model.GetTypeInfo(tuple1);
Assert.Null(tupleType1.Type);
Assert.Equal("(dynamic, dynamic)", tupleType1.ConvertedType.ToTestDisplayString());
Assert.Equal("(dynamic d1, dynamic)", tupleType1.ConvertedType.ToTestDisplayString());
var tuple2 = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().ElementAt(1);
Assert.Equal("(null, d2)", tuple2.ToString());
var tupleType2 = model.GetTypeInfo(tuple2);
Assert.Null(tupleType2.Type);
Assert.Equal("(dynamic, dynamic)", tupleType2.ConvertedType.ToTestDisplayString());
Assert.Equal("(dynamic, dynamic d2)", tupleType2.ConvertedType.ToTestDisplayString());
}
[Fact]
......@@ -3973,6 +3973,92 @@ public override int GetHashCode()
);
}
[Fact]
public void TestElementNames()
{
var source = @"
#pragma warning disable CS0219
using static System.Console;
public class C
{
public static void Main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int x = 5;
int y = 6;
(int x, int y) t1 = (1, 2);
(int, int) t2 = (1, 2);
Write($""{REPLACE}"");
}
}
";
// tuple expression vs tuple expression
validate("t1 == t2");
// tuple expression vs tuple literal
validate("t1 == (x: 1, y: 2)");
validate("t1 == (1, 2)");
validate("(1, 2) == t1");
validate("(x: 1, d) == t1");
validate("t2 == (x: 1, y: 2)",
// (16,25): warning CS8375: The tuple element name 'x' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{t2 == (x: 1, y: 2)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "x: 1").WithArguments("x").WithLocation(16, 25),
// (16,31): warning CS8375: The tuple element name 'y' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{t2 == (x: 1, y: 2)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "y: 2").WithArguments("y").WithLocation(16, 31)
);
// tuple literal vs tuple literal
// - warnings reported on the right when both sides could complain
// - no warnings on inferred names
validate("((a, b), c: 3) == ((1, x: 2), 3)",
// (16,27): warning CS8375: The tuple element name 'c' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{((a, b), c: 3) == ((1, x: 2), 3)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "c: 3").WithArguments("c").WithLocation(16, 27),
// (16,41): warning CS8375: The tuple element name 'x' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{((a, b), c: 3) == ((1, x: 2), 3)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "x: 2").WithArguments("x").WithLocation(16, 41)
);
validate("(a, b) == (a: 1, b: 2)");
validate("(a, b) == (c: 1, d)",
// (16,29): warning CS8375: The tuple element name 'c' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{(a, b) == (c: 1, d)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "c: 1").WithArguments("c").WithLocation(16, 29)
);
validate("(a: 1, b: 2) == (c: 1, d)",
// (16,35): warning CS8375: The tuple element name 'c' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{(a: 1, b: 2) == (c: 1, d)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "c: 1").WithArguments("c").WithLocation(16, 35),
// (16,25): warning CS8375: The tuple element name 'b' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{(a: 1, b: 2) == (c: 1, d)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "b: 2").WithArguments("b").WithLocation(16, 25)
);
validate("(null, b) == (c: null, d: 2)",
// (16,32): warning CS8375: The tuple element name 'c' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{(null, b) == (c: null, d: 2)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "c: null").WithArguments("c").WithLocation(16, 32),
// (16,41): warning CS8375: The tuple element name 'd' is ignored because a different name or no name is specified on the other side of the tuple == or != operator.
// Write($"{(null, b) == (c: null, d: 2)}");
Diagnostic(ErrorCode.WRN_TupleBinopLiteralNameMismatch, "d: 2").WithArguments("d").WithLocation(16, 41)
);
void validate(string expression, params DiagnosticDescription[] diagnostics)
{
var comp = CreateCompilation(source.Replace("REPLACE", expression));
comp.VerifyDiagnostics(diagnostics);
}
}
[Fact]
void TestValueTupleWithObsoleteEqualityOperator()
{
......
......@@ -243,6 +243,7 @@ public void WarningLevel_2()
case ErrorCode.WRN_TupleLiteralNameMismatch:
case ErrorCode.WRN_Experimental:
case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable:
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_MainIgnored:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册