Skip to content

Commit 03cdfb0

Browse files
forkiKevinRansom
authored andcommitted
Create better error message when implementing an interface with overloads (#1907)
* Create better error message when implementing an interface with overloads - fixes #1905 * Add a test for #1905 * Create better error message when implementing an interface with overloads - fixes #1905
1 parent 66829a4 commit 03cdfb0

File tree

7 files changed

+71
-18
lines changed

7 files changed

+71
-18
lines changed

src/fsharp/ErrorResolutionHints.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ let FormatPredictions predictions =
2626
predictions
2727
|> Seq.map (sprintf "%s %s" System.Environment.NewLine)
2828
|> String.concat ""
29-
System.Environment.NewLine + FSComp.SR.undefinedNameRecordLabelDetails() + predictionText
29+
System.Environment.NewLine + FSComp.SR.undefinedNameRecordLabelDetails() + predictionText

src/fsharp/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,3 +1328,4 @@ tcTupleStructMismatch,"One tuple type is a struct tuple, the other is a referenc
13281328
3210,tcNamedActivePattern,"%s is an active pattern and cannot be treated as a discriminated union case with named fields."
13291329
3211,DefaultParameterValueNotAppropriateForArgument,"The default value does not have the same type as the argument. The DefaultParameterValue attribute and any Optional attribute will be ignored. Note: 'null' needs to be annotated with the correct type, e.g. 'DefaultParameterValue(null:obj)'."
13301330
tcGlobalsSystemTypeNotFound,"The system type '%s' was required but no referenced system DLL contained this type"
1331+
3213,typrelMemberHasMultiplePossibleDispatchSlots,"The member '%s' matches multiple overloads of the same method.\nPlease restrict it to one of the following:%s."

src/fsharp/MethodOverrides.fs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -285,24 +285,43 @@ module DispatchSlotChecking =
285285
| [] ->
286286
if not isOptional &&
287287
// Check that no available prior override implements this dispatch slot
288-
not (DispatchSlotIsAlreadyImplemented g amap m availPriorOverridesKeyed dispatchSlot) then
288+
not (DispatchSlotIsAlreadyImplemented g amap m availPriorOverridesKeyed dispatchSlot)
289+
then
289290
// error reporting path
290291
let (CompiledSig (vargtys,_vrty,fvmtps,_)) = CompiledSigOfMeth g amap m dispatchSlot
291-
let noimpl() = if isReqdTyInterface then fail(Error(FSComp.SR.typrelNoImplementationGivenWithSuggestion(NicePrint.stringOfMethInfo amap m denv dispatchSlot), m))
292-
else fail(Error(FSComp.SR.typrelNoImplementationGiven(NicePrint.stringOfMethInfo amap m denv dispatchSlot), m))
293-
match overrides |> List.filter (IsPartialMatch g amap m dispatchSlot) with
292+
let noimpl() =
293+
if isReqdTyInterface then
294+
fail(Error(FSComp.SR.typrelNoImplementationGivenWithSuggestion(NicePrint.stringOfMethInfo amap m denv dispatchSlot), m))
295+
else
296+
fail(Error(FSComp.SR.typrelNoImplementationGiven(NicePrint.stringOfMethInfo amap m denv dispatchSlot), m))
297+
298+
match overrides |> List.filter (IsPartialMatch g amap m dispatchSlot) with
294299
| [] ->
295-
match overrides |> List.filter (fun overrideBy -> IsNameMatch dispatchSlot overrideBy &&
296-
IsImplMatch g dispatchSlot overrideBy) with
300+
let possibleOverrides =
301+
overrides
302+
|> List.filter (fun overrideBy -> IsNameMatch dispatchSlot overrideBy && IsImplMatch g dispatchSlot overrideBy)
303+
304+
match possibleOverrides with
297305
| [] ->
298306
noimpl()
299-
| [ Override(_,_,_,(mtps,_),argTys,_,_,_) as overrideBy ] ->
300-
let error_msg =
301-
if argTys.Length <> vargtys.Length then FSComp.SR.typrelMemberDoesNotHaveCorrectNumberOfArguments(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot)
302-
elif mtps.Length <> fvmtps.Length then FSComp.SR.typrelMemberDoesNotHaveCorrectNumberOfTypeParameters(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot)
303-
elif not (IsTyparKindMatch g amap m dispatchSlot overrideBy) then FSComp.SR.typrelMemberDoesNotHaveCorrectKindsOfGenericParameters(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot)
304-
else FSComp.SR.typrelMemberCannotImplement(FormatOverride denv overrideBy, NicePrint.stringOfMethInfo amap m denv dispatchSlot, FormatMethInfoSig g amap m denv dispatchSlot)
305-
fail(Error(error_msg, overrideBy.Range))
307+
| [ Override(_,_,_,(mtps,_),argTys,_,_,_) as overrideBy ] ->
308+
let possibleDispatchSlots =
309+
dispatchSlots
310+
|> List.filter (fun (RequiredSlot(dispatchSlot,_)) -> IsNameMatch dispatchSlot overrideBy && IsImplMatch g dispatchSlot overrideBy)
311+
|> List.length
312+
313+
if possibleDispatchSlots > 1 then
314+
// Error will be reported below in CheckOverridesAreAllUsedOnce
315+
()
316+
317+
elif argTys.Length <> vargtys.Length then
318+
fail(Error(FSComp.SR.typrelMemberDoesNotHaveCorrectNumberOfArguments(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot), overrideBy.Range))
319+
elif mtps.Length <> fvmtps.Length then
320+
fail(Error(FSComp.SR.typrelMemberDoesNotHaveCorrectNumberOfTypeParameters(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot), overrideBy.Range))
321+
elif not (IsTyparKindMatch g amap m dispatchSlot overrideBy) then
322+
fail(Error(FSComp.SR.typrelMemberDoesNotHaveCorrectKindsOfGenericParameters(FormatOverride denv overrideBy, FormatMethInfoSig g amap m denv dispatchSlot), overrideBy.Range))
323+
else
324+
fail(Error(FSComp.SR.typrelMemberCannotImplement(FormatOverride denv overrideBy, NicePrint.stringOfMethInfo amap m denv dispatchSlot, FormatMethInfoSig g amap m denv dispatchSlot), overrideBy.Range))
306325
| overrideBy :: _ ->
307326
errorR(Error(FSComp.SR.typrelOverloadNotFound(FormatMethInfoSig g amap m denv dispatchSlot, FormatMethInfoSig g amap m denv dispatchSlot),overrideBy.Range))
308327

@@ -338,10 +357,17 @@ module DispatchSlotChecking =
338357
errorR(OverrideDoesntOverride(denv,overrideBy,Some dispatchSlot,g,amap,m))
339358
| _ ->
340359
match relevantVirts |> List.filter (fun dispatchSlot -> IsNameMatch dispatchSlot overrideBy) with
360+
| [] -> errorR(OverrideDoesntOverride(denv,overrideBy,None,g,amap,m))
341361
| [dispatchSlot] ->
342362
errorR(OverrideDoesntOverride(denv, overrideBy, Some dispatchSlot, g, amap, m))
343-
| _ ->
344-
errorR(OverrideDoesntOverride(denv,overrideBy,None,g,amap,m))
363+
| possibleDispatchSlots ->
364+
let details =
365+
possibleDispatchSlots
366+
|> List.map (fun dispatchSlot -> FormatMethInfoSig g amap m denv dispatchSlot)
367+
|> Seq.map (sprintf "%s %s" System.Environment.NewLine)
368+
|> String.concat ""
369+
370+
errorR(Error(FSComp.SR.typrelMemberHasMultiplePossibleDispatchSlots(FormatOverride denv overrideBy, details), overrideBy.Range))
345371

346372

347373
| [dispatchSlot] ->

tests/fsharp/typecheck/sigs/neg10.bsl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ neg10.fs(131,23,131,29): typecheck error FS0906: Type abbreviations cannot have
6161

6262
neg10.fs(169,32,169,35): typecheck error FS0035: This construct is deprecated: This form of object expression is not used in F#. Use 'member this.MemberName ... = ...' to define member implementations in object expressions.
6363

64-
neg10.fs(169,32,169,33): typecheck error FS0017: The member 'X : unit -> 'a' does not have the correct type to override any given virtual method
64+
neg10.fs(169,32,169,33): typecheck error FS3213: The member 'X : unit -> 'a' matches multiple overloads of the same method.
65+
Please restrict it to one of the following:
66+
67+
X : unit -> 'a
68+
69+
X : unit -> 'a.
6570

6671
neg10.fs(169,19,169,26): typecheck error FS0783: At least one override did not correctly implement its corresponding abstract member
6772

tests/fsharp/typecheck/sigs/neg26.bsl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ neg26.fs(27,13,27,66): typecheck error FS0366: No implementation was given for '
55

66
neg26.fs(40,27,40,32): typecheck error FS0361: The override 'Meth1 : int -> int' implements more than one abstract slot, e.g. 'abstract member ITestSub.Meth1 : int -> int' and 'abstract member ITest.Meth1 : int -> int'
77

8-
neg26.fs(53,27,53,32): typecheck error FS0017: The member 'Meth1 : 'a -> 'a' does not have the correct type to override any given virtual method
8+
neg26.fs(53,27,53,32): typecheck error FS3213: The member 'Meth1 : 'a -> 'a' matches multiple overloads of the same method.
9+
Please restrict it to one of the following:
10+
11+
Meth1 : int -> int
12+
13+
Meth1 : int -> int.
914

1015
neg26.fs(52,15,52,23): typecheck error FS0783: At least one override did not correctly implement its corresponding abstract member
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// #Warnings
2+
//<Expects status="Error" id="FS3213">The member 'Bar</Expect>
3+
//<Expects>Please restrict it to one of the following:</Expects>
4+
//<Expects>Bar : double -> int</Expects>
5+
//<Expects>Bar : int -> int</Expects>
6+
7+
type IOverload =
8+
abstract member Bar : int -> int
9+
abstract member Bar : double -> int
10+
11+
type Overload =
12+
interface IOverload with
13+
override __.Bar _ = 1
14+
15+
exit 0

tests/fsharpqa/Source/Warnings/env.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
SOURCE=RuntimeTypeTestInPattern.fs # RuntimeTypeTestInPattern.fs
3939
SOURCE=RuntimeTypeTestInPattern2.fs # RuntimeTypeTestInPattern2.fs
4040
SOURCE=WarnIfExpressionResultUnused.fs # WarnIfExpressionResultUnused.fs
41+
SOURCE=MemberHasMultiplePossibleDispatchSlots.fs # MemberHasMultiplePossibleDispatchSlots.fs
4142
SOURCE=Repro1548.fs SCFLAGS="-r:Repro1548.dll" # Repro1548.fs
4243
SOURCE=WarnIfPossibleAssignment.fs
4344
SOURCE=WarnIfPossibleAssignmentToMutable.fs

0 commit comments

Comments
 (0)