From bfbc3e9822dd05f7dbf8711c0699f95127208398 Mon Sep 17 00:00:00 2001
From: Steffen Forkmann <steffen.forkmann@msu-solutions.de>
Date: Tue, 31 May 2016 15:54:16 +0200
Subject: [PATCH 1/2] Suggest a function to implement

---
 src/fsharp/FSComp.txt                              |  2 +-
 src/fsharp/TypeChecker.fs                          |  8 +++++++-
 .../NoMatchingAbstractMethodWithSameName.fs        | 14 ++++++++++++++
 tests/fsharpqa/Source/Warnings/env.lst             |  1 +
 4 files changed, 23 insertions(+), 2 deletions(-)
 create mode 100644 tests/fsharpqa/Source/Warnings/NoMatchingAbstractMethodWithSameName.fs

diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt
index 5bbff5d3ffa..1ff34fb9e6b 100644
--- a/src/fsharp/FSComp.txt
+++ b/src/fsharp/FSComp.txt
@@ -588,7 +588,7 @@ tcExpressionWithIfRequiresParenthesis,"This list or array expression includes an
 764,tcFieldRequiresAssignment,"No assignment given for field '%s' of type '%s'"
 765,tcExtraneousFieldsGivenValues,"Extraneous fields have been given values"
 766,tcObjectExpressionsCanOnlyOverrideAbstractOrVirtual,"Only overrides of abstract and virtual members may be specified in object expressions"
-767,tcNoAbstractOrVirtualMemberFound,"The member '%s' does not correspond to any abstract or virtual method available to override or implement"
+767,tcNoAbstractOrVirtualMemberFound,"The member '%s' does not correspond to any abstract or virtual method available to override or implement.%s"
 768,tcArgumentArityMismatch,"The member '%s' does not accept the correct number of arguments, %d arguments are expected"
 769,tcArgumentArityMismatchOneOverload,"The member '%s' does not accept the correct number of arguments. One overload accepts %d arguments."
 770,tcSimpleMethodNameRequired,"A simple method name is required here"
diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs
index c8cfc054800..4599cd304ad 100755
--- a/src/fsharp/TypeChecker.fs
+++ b/src/fsharp/TypeChecker.fs
@@ -6209,7 +6209,13 @@ and FreshenObjExprAbstractSlot cenv (_env: TcEnv) implty virtNameAndArityPairs (
         let absSlotsByName = List.filter (fst >> fst >> (=) bindName) virtNameAndArityPairs
         
         match absSlotsByName with 
-        | []              -> errorR(Error(FSComp.SR.tcNoAbstractOrVirtualMemberFound(bindName),mBinding))
+        | []              ->
+            let predictions =
+                virtNameAndArityPairs
+                |> List.map (fst >> fst)
+                |> ErrorResolutionHints.FilterPredictions bindName
+
+            errorR(Error(FSComp.SR.tcNoAbstractOrVirtualMemberFound(bindName, ErrorResolutionHints.FormatPredictions predictions),mBinding))
         | [(_,absSlot:MethInfo)]     -> errorR(Error(FSComp.SR.tcArgumentArityMismatch(bindName, (List.sum absSlot.NumArgs)),mBinding))
         | (_,absSlot:MethInfo) :: _  -> errorR(Error(FSComp.SR.tcArgumentArityMismatchOneOverload(bindName, (List.sum absSlot.NumArgs)),mBinding))
         
diff --git a/tests/fsharpqa/Source/Warnings/NoMatchingAbstractMethodWithSameName.fs b/tests/fsharpqa/Source/Warnings/NoMatchingAbstractMethodWithSameName.fs
new file mode 100644
index 00000000000..a59f863a774
--- /dev/null
+++ b/tests/fsharpqa/Source/Warnings/NoMatchingAbstractMethodWithSameName.fs
@@ -0,0 +1,14 @@
+// #Warnings
+//<Expects status="Error" span="(11,14)" id="FS0767">The member 'Function' does not correspond to any abstract or virtual method available to override or implement.</Expects>
+//<Expects>MyFunction</Expects>
+
+type IInterface =
+    abstract MyFunction : int32 * int32 -> unit
+    abstract SomeOtherFunction : int32 * int32 -> unit
+
+let x = 
+  { new IInterface with
+      member this.Function (i, j) = ()
+  }
+
+exit 0
\ No newline at end of file
diff --git a/tests/fsharpqa/Source/Warnings/env.lst b/tests/fsharpqa/Source/Warnings/env.lst
index 89651c646aa..ac2d8543e5b 100644
--- a/tests/fsharpqa/Source/Warnings/env.lst
+++ b/tests/fsharpqa/Source/Warnings/env.lst
@@ -5,6 +5,7 @@
 	SOURCE=CommaInRecCtor.fs # CommaInRecCtor.fs
 	SOURCE=ValidCommaInRecCtor.fs # ValidCommaInRecCtor.fs
 	SOURCE=ElseBranchHasWrongType.fs # ElseBranchHasWrongType.fs
+	SOURCE=NoMatchingAbstractMethodWithSameName.fs # NoMatchingAbstractMethodWithSameName.fs
 	SOURCE=MissingExpressionAfterLet.fs # MissingExpressionAfterLet.fs
 	SOURCE=AssignmentOnImmutable.fs # AssignmentOnImmutable.fs
 	SOURCE=UpcastInsteadOfDowncast.fs # UpcastInsteadOfDowncast.fs

From 3e7ddeeffe6e454d5107e291151a73aa4e741b4f Mon Sep 17 00:00:00 2001
From: Steffen Forkmann <steffen.forkmann@msu-solutions.de>
Date: Thu, 2 Jun 2016 12:33:39 +0200
Subject: [PATCH 2/2] If the member is not abstract then give better error

---
 src/fsharp/FSComp.txt                              |  1 +
 src/fsharp/TypeChecker.fs                          | 14 +++++++++++---
 tests/fsharp/typecheck/sigs/neg10.bsl              | 12 +++++++++++-
 .../ObjectExpressions/E_MembersMustBeVirtual01.fs  |  2 +-
 .../MatchingMethodWithSameNameIsNotAbstract.fs     | 14 ++++++++++++++
 tests/fsharpqa/Source/Warnings/env.lst             |  1 +
 6 files changed, 39 insertions(+), 5 deletions(-)
 create mode 100644 tests/fsharpqa/Source/Warnings/MatchingMethodWithSameNameIsNotAbstract.fs

diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt
index 1ff34fb9e6b..7ca7ce14e80 100644
--- a/src/fsharp/FSComp.txt
+++ b/src/fsharp/FSComp.txt
@@ -589,6 +589,7 @@ tcExpressionWithIfRequiresParenthesis,"This list or array expression includes an
 765,tcExtraneousFieldsGivenValues,"Extraneous fields have been given values"
 766,tcObjectExpressionsCanOnlyOverrideAbstractOrVirtual,"Only overrides of abstract and virtual members may be specified in object expressions"
 767,tcNoAbstractOrVirtualMemberFound,"The member '%s' does not correspond to any abstract or virtual method available to override or implement.%s"
+767,tcMemberFoundIsNotAbstractOrVirtual,"The type %s contains the member '%s' but it is not a virtual or abstract method that is available to override or implement.%s"
 768,tcArgumentArityMismatch,"The member '%s' does not accept the correct number of arguments, %d arguments are expected"
 769,tcArgumentArityMismatchOneOverload,"The member '%s' does not accept the correct number of arguments. One overload accepts %d arguments."
 770,tcSimpleMethodNameRequired,"A simple method name is required here"
diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs
index 4599cd304ad..fd8a876b6c0 100755
--- a/src/fsharp/TypeChecker.fs
+++ b/src/fsharp/TypeChecker.fs
@@ -6202,20 +6202,28 @@ and GetNameAndArityOfObjExprBinding _cenv _env b =
         lookPat pat
 
 
-and FreshenObjExprAbstractSlot cenv (_env: TcEnv) implty virtNameAndArityPairs (bind,bindAttribs,bindName,absSlots:(_ * MethInfo) list) = 
+and FreshenObjExprAbstractSlot cenv (_env: TcEnv) (implty:TType) virtNameAndArityPairs (bind,bindAttribs,bindName,absSlots:(_ * MethInfo) list) = 
     let (NormalizedBinding (_,_,_,_,_,_,synTyparDecls,_,_,_,mBinding,_)) = bind 
     match absSlots with 
     | [] when not (CompileAsEvent cenv.g bindAttribs) -> 
         let absSlotsByName = List.filter (fst >> fst >> (=) bindName) virtNameAndArityPairs
         
         match absSlotsByName with 
-        | []              ->
+        | [] ->
+            let tcref = tcrefOfAppTy cenv.g implty
+            let containsNonAbstractMemberWithSameName = 
+                tcref.MembersOfFSharpTyconByName
+                |> Seq.exists (fun kv -> kv.Value |> List.exists (fun valRef -> valRef.DisplayName = bindName))
+
             let predictions =
                 virtNameAndArityPairs
                 |> List.map (fst >> fst)
                 |> ErrorResolutionHints.FilterPredictions bindName
 
-            errorR(Error(FSComp.SR.tcNoAbstractOrVirtualMemberFound(bindName, ErrorResolutionHints.FormatPredictions predictions),mBinding))
+            if containsNonAbstractMemberWithSameName then
+                errorR(Error(FSComp.SR.tcMemberFoundIsNotAbstractOrVirtual(tcref.DisplayName, bindName, ErrorResolutionHints.FormatPredictions predictions),mBinding))                
+            else
+                errorR(Error(FSComp.SR.tcNoAbstractOrVirtualMemberFound(bindName, ErrorResolutionHints.FormatPredictions predictions),mBinding))
         | [(_,absSlot:MethInfo)]     -> errorR(Error(FSComp.SR.tcArgumentArityMismatch(bindName, (List.sum absSlot.NumArgs)),mBinding))
         | (_,absSlot:MethInfo) :: _  -> errorR(Error(FSComp.SR.tcArgumentArityMismatchOneOverload(bindName, (List.sum absSlot.NumArgs)),mBinding))
         
diff --git a/tests/fsharp/typecheck/sigs/neg10.bsl b/tests/fsharp/typecheck/sigs/neg10.bsl
index ca631ed99f7..ba47d7e2529 100644
--- a/tests/fsharp/typecheck/sigs/neg10.bsl
+++ b/tests/fsharp/typecheck/sigs/neg10.bsl
@@ -69,7 +69,17 @@ neg10.fs(174,9,175,20): typecheck error FS0951: Literal enumerations must have t
 
 neg10.fs(180,10,180,11): typecheck error FS0866: Interfaces cannot contain definitions of object constructors
 
-neg10.fs(193,39,193,46): typecheck error FS0767: The member 'MyX' does not correspond to any abstract or virtual method available to override or implement
+neg10.fs(193,39,193,46): typecheck error FS0767: The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement.
+
+Maybe you want one of the following:
+
+   Equals
+
+   ToString
+
+   Finalize
+
+   GetHashCode
 
 neg10.fs(193,41,193,44): typecheck error FS0017: The member 'MyX : unit -> int' does not have the correct type to override any given virtual method
 
diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ObjectExpressions/E_MembersMustBeVirtual01.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ObjectExpressions/E_MembersMustBeVirtual01.fs
index 5950ee0ed00..41badad221f 100644
--- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ObjectExpressions/E_MembersMustBeVirtual01.fs
+++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/ObjectExpressions/E_MembersMustBeVirtual01.fs
@@ -1,7 +1,7 @@
 // #Regression #Conformance #DataExpressions #ObjectConstructors 
 // FSB 1683, dispatch slot checking in object expression manages to match non-virtual member
 
-//<Expects id="FS0767" status="error" span="(11,35-11,42)">The member 'MyX' does not correspond to any abstract or virtual method available to override or implement$</Expects>
+//<Expects id="FS0767" status="error" span="(11,35-11,42)">The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement.</Expects>
 //<Expects id="FS0017" status="error" span="(11,37-11,40)">The member 'MyX : unit -> int' does not have the correct type to override any given virtual method$</Expects>
 //<Expects id="FS0783" status="error" span="(11,16-11,19)">At least one override did not correctly implement its corresponding abstract member$</Expects>
 
diff --git a/tests/fsharpqa/Source/Warnings/MatchingMethodWithSameNameIsNotAbstract.fs b/tests/fsharpqa/Source/Warnings/MatchingMethodWithSameNameIsNotAbstract.fs
new file mode 100644
index 00000000000..36275ff0e92
--- /dev/null
+++ b/tests/fsharpqa/Source/Warnings/MatchingMethodWithSameNameIsNotAbstract.fs
@@ -0,0 +1,14 @@
+// #Warnings
+//<Expects status="Error" span="(11,16)" id="FS0767">The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement.</Expects>
+//<Expects>ToString</Expects>
+
+type Foo(x : int) =
+  member v.MyX() = x
+  
+let foo =  
+    { new Foo(3) 
+        with
+        member v.MyX() = 4 }
+
+
+exit 0
\ No newline at end of file
diff --git a/tests/fsharpqa/Source/Warnings/env.lst b/tests/fsharpqa/Source/Warnings/env.lst
index ac2d8543e5b..54b35516a04 100644
--- a/tests/fsharpqa/Source/Warnings/env.lst
+++ b/tests/fsharpqa/Source/Warnings/env.lst
@@ -5,6 +5,7 @@
 	SOURCE=CommaInRecCtor.fs # CommaInRecCtor.fs
 	SOURCE=ValidCommaInRecCtor.fs # ValidCommaInRecCtor.fs
 	SOURCE=ElseBranchHasWrongType.fs # ElseBranchHasWrongType.fs
+	SOURCE=MatchingMethodWithSameNameIsNotAbstract.fs # MatchingMethodWithSameNameIsNotAbstract.fs
 	SOURCE=NoMatchingAbstractMethodWithSameName.fs # NoMatchingAbstractMethodWithSameName.fs
 	SOURCE=MissingExpressionAfterLet.fs # MissingExpressionAfterLet.fs
 	SOURCE=AssignmentOnImmutable.fs # AssignmentOnImmutable.fs
