Skip to content

Commit

Permalink
Report better errors when multiple invalid constraints are combined
Browse files Browse the repository at this point in the history
Instead of reporting that a constraint must come first, if there are multiple constraints that must come first, we now report that these constraints cannot be combined. We also suppress these errors if a previous error about inherited constraints was reported. Fixes #45141.
  • Loading branch information
333fred committed Jun 13, 2020
1 parent 18b41d0 commit 7ca0c7e
Show file tree
Hide file tree
Showing 20 changed files with 543 additions and 33 deletions.
32 changes: 28 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo
bool hasTypeLikeConstraint = false;
bool reportedOverrideWithConstraints = false;
MessageID? firstTypeConstraintString = null;

for (int i = 0, n = constraintsSyntax.Count; i < n; i++)
{
Expand All @@ -137,14 +138,18 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai

if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation());
if (!reportedOverrideWithConstraints)
{
reportNotFirstOrCannotCombine(diagnostics, syntax.GetFirstToken().GetLocation(), ErrorCode.ERR_RefValBoundMustBeFirst, MessageID.IDS_Class, firstTypeConstraintString);
}

if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0)
{
continue;
}
}

firstTypeConstraintString ??= MessageID.IDS_Class;
var constraintSyntax = (ClassOrStructConstraintSyntax)syntax;
SyntaxToken questionToken = constraintSyntax.QuestionToken;
if (questionToken.IsKind(SyntaxKind.QuestionToken))
Expand Down Expand Up @@ -175,14 +180,18 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai

if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation());
if (!reportedOverrideWithConstraints)
{
reportNotFirstOrCannotCombine(diagnostics, syntax.GetFirstToken().GetLocation(), ErrorCode.ERR_RefValBoundMustBeFirst, MessageID.IDS_Struct, firstTypeConstraintString);
}

if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0)
{
continue;
}
}

firstTypeConstraintString ??= MessageID.IDS_Struct;
constraints |= TypeParameterConstraintKind.ValueType;
continue;
case SyntaxKind.ConstructorConstraint:
Expand Down Expand Up @@ -233,10 +242,12 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
case ConstraintContextualKeyword.Unmanaged:
if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, typeSyntax.GetLocation());
reportNotFirstOrCannotCombine(diagnostics, typeSyntax.GetLocation(), ErrorCode.ERR_UnmanagedConstraintMustBeFirst, MessageID.IDS_Unmanaged, firstTypeConstraintString);
continue;
}

firstTypeConstraintString ??= MessageID.IDS_Unmanaged;

// This should produce diagnostics if the types are missing
GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType, diagnostics, typeSyntax);
GetSpecialType(SpecialType.System_ValueType, diagnostics, typeSyntax);
Expand All @@ -247,9 +258,10 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
case ConstraintContextualKeyword.NotNull:
if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_NotNullConstraintMustBeFirst, typeSyntax.GetLocation());
reportNotFirstOrCannotCombine(diagnostics, typeSyntax.GetLocation(), ErrorCode.ERR_NotNullConstraintMustBeFirst, MessageID.IDS_Notnull, firstTypeConstraintString);
}

firstTypeConstraintString ??= MessageID.IDS_Notnull;
constraints |= TypeParameterConstraintKind.NotNull;
continue;

Expand Down Expand Up @@ -286,6 +298,18 @@ static void reportOverrideWithConstraints(ref bool reportedOverrideWithConstrain
reportedOverrideWithConstraints = true;
}
}

static void reportNotFirstOrCannotCombine(DiagnosticBag diagnostics, Location constraintLocation, ErrorCode notFirstDiagnostic, MessageID currentConstraintString, MessageID? firstConstraintString)
{
if (firstConstraintString is MessageID firstConstraint)
{
diagnostics.Add(ErrorCode.ERR_CannotCombineTypeConstraints, constraintLocation, currentConstraintString.Localize(), firstConstraint.Localize());
}
else
{
diagnostics.Add(notFirstDiagnostic, constraintLocation);
}
}
}

internal ImmutableArray<TypeParameterConstraintClause> GetDefaultTypeParameterConstraintClauses(TypeParameterListSyntax typeParameterList)
Expand Down
15 changes: 15 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6262,4 +6262,19 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_CopyConstructorMustInvokeBaseCopyConstructor" xml:space="preserve">
<value>A copy constructor in a record must call a copy constructor of the base, or a parameterless object constructor if the record inherits from object.</value>
</data>
<data name="IDS_Class" xml:space="preserve">
<value>class</value>
</data>
<data name="IDS_Struct" xml:space="preserve">
<value>struct</value>
</data>
<data name="IDS_Unmanaged" xml:space="preserve">
<value>unmanaged</value>
</data>
<data name="IDS_Notnull" xml:space="preserve">
<value>notnull</value>
</data>
<data name="ERR_CannotCombineTypeConstraints" xml:space="preserve">
<value>The '{0}' constraint cannot be combined with the '{1}' constraint</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,7 @@ internal enum ErrorCode
ERR_BadRecordMemberForPositionalParameter = 8866,
ERR_NoCopyConstructorInBaseType = 8867,
ERR_CopyConstructorMustInvokeBaseCopyConstructor = 8868,
ERR_CannotCombineTypeConstraints = 8869,

#endregion

Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ internal enum MessageID
IDS_FeatureInitOnlySetters = MessageBase + 12781,
IDS_FeatureRecords = MessageBase + 12782,
IDS_FeatureNullPointerConstantPattern = MessageBase + 12783,
IDS_Class = MessageBase + 12784,
IDS_Struct = MessageBase + 12785,
IDS_Unmanaged = MessageBase + 12786,
IDS_Notnull = MessageBase + 12787,
}

// Message IDs may refer to strings that need to be localized.
Expand Down
25 changes: 25 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
<target state="translated">Chyba syntaxe příkazového řádku: {0} není platná hodnota možnosti {1}. Hodnota musí mít tvar {2}.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotCombineTypeConstraints">
<source>The '{0}' constraint cannot be combined with the '{1}' constraint</source>
<target state="new">The '{0}' constraint cannot be combined with the '{1}' constraint</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
Expand Down Expand Up @@ -1114,6 +1119,11 @@
</target>
<note>Visual C# Compiler Options</note>
</trans-unit>
<trans-unit id="IDS_Class">
<source>class</source>
<target state="new">class</target>
<note />
</trans-unit>
<trans-unit id="IDS_DefaultInterfaceImplementation">
<source>default interface implementation</source>
<target state="translated">implementace výchozího rozhraní</target>
Expand Down Expand Up @@ -1364,11 +1374,21 @@
<target state="translated">&lt;null&gt;</target>
<note />
</trans-unit>
<trans-unit id="IDS_Notnull">
<source>notnull</source>
<target state="new">notnull</target>
<note />
</trans-unit>
<trans-unit id="IDS_OverrideWithConstraints">
<source>constraints for override and explicit interface implementation methods</source>
<target state="translated">omezení pro metody přepsání a explicitní implementace rozhraní</target>
<note />
</trans-unit>
<trans-unit id="IDS_Struct">
<source>struct</source>
<target state="new">struct</target>
<note />
</trans-unit>
<trans-unit id="IDS_ThrowExpression">
<source>&lt;throw expression&gt;</source>
<target state="translated">&lt;výraz throw&gt;</target>
Expand All @@ -1389,6 +1409,11 @@
<target state="new">top-level statements</target>
<note />
</trans-unit>
<trans-unit id="IDS_Unmanaged">
<source>unmanaged</source>
<target state="new">unmanaged</target>
<note />
</trans-unit>
<trans-unit id="IDS_XMLIGNORED">
<source>&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</source>
<target state="translated">&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</target>
Expand Down
25 changes: 25 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
<target state="translated">Fehler in der Befehlszeilensyntax: "{0}" ist kein gültiger Wert für die Option "{1}". Der Wert muss im Format "{2}" vorliegen.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotCombineTypeConstraints">
<source>The '{0}' constraint cannot be combined with the '{1}' constraint</source>
<target state="new">The '{0}' constraint cannot be combined with the '{1}' constraint</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
Expand Down Expand Up @@ -1114,6 +1119,11 @@
</target>
<note>Visual C# Compiler Options</note>
</trans-unit>
<trans-unit id="IDS_Class">
<source>class</source>
<target state="new">class</target>
<note />
</trans-unit>
<trans-unit id="IDS_DefaultInterfaceImplementation">
<source>default interface implementation</source>
<target state="translated">Standardschnittstellenimplementierung</target>
Expand Down Expand Up @@ -1364,11 +1374,21 @@
<target state="translated">&lt;NULL&gt;</target>
<note />
</trans-unit>
<trans-unit id="IDS_Notnull">
<source>notnull</source>
<target state="new">notnull</target>
<note />
</trans-unit>
<trans-unit id="IDS_OverrideWithConstraints">
<source>constraints for override and explicit interface implementation methods</source>
<target state="translated">Einschränkungen für Außerkraftsetzung und explizite Schnittstellenimplementierungsmethoden</target>
<note />
</trans-unit>
<trans-unit id="IDS_Struct">
<source>struct</source>
<target state="new">struct</target>
<note />
</trans-unit>
<trans-unit id="IDS_ThrowExpression">
<source>&lt;throw expression&gt;</source>
<target state="translated">throw-Ausdruck</target>
Expand All @@ -1389,6 +1409,11 @@
<target state="new">top-level statements</target>
<note />
</trans-unit>
<trans-unit id="IDS_Unmanaged">
<source>unmanaged</source>
<target state="new">unmanaged</target>
<note />
</trans-unit>
<trans-unit id="IDS_XMLIGNORED">
<source>&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</source>
<target state="translated">&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</target>
Expand Down
25 changes: 25 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
<target state="translated">Error de sintaxis de la línea de comandos: "{0}" no es un valor válido para la opción "{1}". El valor debe tener el formato "{2}".</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotCombineTypeConstraints">
<source>The '{0}' constraint cannot be combined with the '{1}' constraint</source>
<target state="new">The '{0}' constraint cannot be combined with the '{1}' constraint</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
Expand Down Expand Up @@ -1114,6 +1119,11 @@
</target>
<note>Visual C# Compiler Options</note>
</trans-unit>
<trans-unit id="IDS_Class">
<source>class</source>
<target state="new">class</target>
<note />
</trans-unit>
<trans-unit id="IDS_DefaultInterfaceImplementation">
<source>default interface implementation</source>
<target state="translated">implementación de interfaz predeterminada</target>
Expand Down Expand Up @@ -1364,11 +1374,21 @@
<target state="translated">&lt;NULL&gt;</target>
<note />
</trans-unit>
<trans-unit id="IDS_Notnull">
<source>notnull</source>
<target state="new">notnull</target>
<note />
</trans-unit>
<trans-unit id="IDS_OverrideWithConstraints">
<source>constraints for override and explicit interface implementation methods</source>
<target state="translated">restricciones para métodos de implementación de interfaz explícita e invalidación</target>
<note />
</trans-unit>
<trans-unit id="IDS_Struct">
<source>struct</source>
<target state="new">struct</target>
<note />
</trans-unit>
<trans-unit id="IDS_ThrowExpression">
<source>&lt;throw expression&gt;</source>
<target state="translated">&lt;expresión throw&gt;</target>
Expand All @@ -1389,6 +1409,11 @@
<target state="new">top-level statements</target>
<note />
</trans-unit>
<trans-unit id="IDS_Unmanaged">
<source>unmanaged</source>
<target state="new">unmanaged</target>
<note />
</trans-unit>
<trans-unit id="IDS_XMLIGNORED">
<source>&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</source>
<target state="translated">&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</target>
Expand Down
25 changes: 25 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
<target state="translated">Erreur de syntaxe de ligne de commande : '{0}' est une valeur non valide pour l'option '{1}'. La valeur doit se présenter sous la forme '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotCombineTypeConstraints">
<source>The '{0}' constraint cannot be combined with the '{1}' constraint</source>
<target state="new">The '{0}' constraint cannot be combined with the '{1}' constraint</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
Expand Down Expand Up @@ -1114,6 +1119,11 @@
</target>
<note>Visual C# Compiler Options</note>
</trans-unit>
<trans-unit id="IDS_Class">
<source>class</source>
<target state="new">class</target>
<note />
</trans-unit>
<trans-unit id="IDS_DefaultInterfaceImplementation">
<source>default interface implementation</source>
<target state="translated">implémentation d'interface par défaut</target>
Expand Down Expand Up @@ -1364,11 +1374,21 @@
<target state="translated">&lt;Null&gt;</target>
<note />
</trans-unit>
<trans-unit id="IDS_Notnull">
<source>notnull</source>
<target state="new">notnull</target>
<note />
</trans-unit>
<trans-unit id="IDS_OverrideWithConstraints">
<source>constraints for override and explicit interface implementation methods</source>
<target state="translated">contraintes des méthodes d'implémentation d'interface par remplacement et explicites</target>
<note />
</trans-unit>
<trans-unit id="IDS_Struct">
<source>struct</source>
<target state="new">struct</target>
<note />
</trans-unit>
<trans-unit id="IDS_ThrowExpression">
<source>&lt;throw expression&gt;</source>
<target state="translated">&lt;expression throw&gt;</target>
Expand All @@ -1389,6 +1409,11 @@
<target state="new">top-level statements</target>
<note />
</trans-unit>
<trans-unit id="IDS_Unmanaged">
<source>unmanaged</source>
<target state="new">unmanaged</target>
<note />
</trans-unit>
<trans-unit id="IDS_XMLIGNORED">
<source>&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</source>
<target state="translated">&lt;!-- Badly formed XML comment ignored for member "{0}" --&gt;</target>
Expand Down
Loading

0 comments on commit 7ca0c7e

Please sign in to comment.