Skip to content

Commit

Permalink
implement check and codefix for mismatched parameters (#3527)
Browse files Browse the repository at this point in the history
Issue opened:  #3539
  • Loading branch information
dsyme authored and KevinRansom committed Sep 1, 2017
1 parent 3bec369 commit 0f259ed
Show file tree
Hide file tree
Showing 25 changed files with 223 additions and 73 deletions.
12 changes: 9 additions & 3 deletions src/absil/il.fs
Expand Up @@ -675,27 +675,33 @@ and [<RequireQualifiedAccess; StructuralEquality; StructuralComparison>]

member x.QualifiedNameWithNoShortPrimaryAssembly =
x.AddQualifiedNameExtensionWithNoShortPrimaryAssembly(x.BasicQualifiedName)

member x.TypeSpec =
match x with
| ILType.Boxed tr | ILType.Value tr -> tr
| _ -> invalidOp "not a nominal type"

member x.Boxity =
match x with
| ILType.Boxed _ -> AsObject
| ILType.Value _ -> AsValue
| _ -> invalidOp "not a nominal type"

member x.TypeRef =
match x with
| ILType.Boxed tspec | ILType.Value tspec -> tspec.TypeRef
| _ -> invalidOp "not a nominal type"

member x.IsNominal =
match x with
| ILType.Boxed _ | ILType.Value _ -> true
| _ -> false

member x.GenericArgs =
match x with
| ILType.Boxed tspec | ILType.Value tspec -> tspec.GenericArgs
| _ -> []

member x.IsTyvar =
match x with
| ILType.TypeVar _ -> true | _ -> false
Expand Down Expand Up @@ -843,9 +849,9 @@ type ILAttribute =

[<NoEquality; NoComparison; Sealed>]
type ILAttributes(f: unit -> ILAttribute[]) =
let mutable array = InlineDelayInit<_>(f)
member x.AsArray = array.Value
member x.AsList = x.AsArray |> Array.toList
let mutable array = InlineDelayInit<_>(f)
member x.AsArray = array.Value
member x.AsList = x.AsArray |> Array.toList

type ILCodeLabel = int

Expand Down
1 change: 1 addition & 0 deletions src/fsharp/CompileOps.fs
Expand Up @@ -399,6 +399,7 @@ let warningOn err level specificWarnOn =
// Some specific warnings are never on by default, i.e. unused variable warnings
match n with
| 1182 -> false // chkUnusedValue - off by default
| 3218 -> false // ArgumentsInSigAndImplMismatch - off by default
| 3180 -> false // abImplicitHeapAllocation - off by default
| _ -> level >= GetWarningLevel err

Expand Down
1 change: 1 addition & 0 deletions src/fsharp/FSComp.txt
Expand Up @@ -1420,3 +1420,4 @@ notAFunctionButMaybeIndexerWithName,"This value is not a function and cannot be
notAFunctionButMaybeIndexer,"This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?"
3217,notAFunctionButMaybeIndexerErrorCode,""
notAFunctionButMaybeDeclaration,"This value is not a function and cannot be applied. Did you forget to terminate a declaration?"
3218,ArgumentsInSigAndImplMismatch,"The argument names in the signature '%s' and implementation '%s' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling."
12 changes: 6 additions & 6 deletions src/fsharp/FSharp.Core/prim-types.fs
Expand Up @@ -2781,21 +2781,21 @@ namespace Microsoft.FSharp.Core
inherit FSharpFunc<'T,('U -> 'V)>()
abstract Invoke : 'T * 'U -> 'V
override f.Invoke(t) = (fun u -> f.Invoke(t,u))
static member Adapt(f : 'T -> 'U -> 'V) =
match box f with
static member Adapt(func : 'T -> 'U -> 'V) =
match box func with
// Does it take two arguments without side effect?
| :? FSharpFunc<'T,'U,'V> as f -> f

| _ -> { new FSharpFunc<'T,'U,'V>() with
member x.Invoke(t,u) = (retype f : FSharpFunc<'T,FSharpFunc<'U,'V>>).Invoke(t).Invoke(u) }
member x.Invoke(t,u) = (retype func : FSharpFunc<'T,FSharpFunc<'U,'V>>).Invoke(t).Invoke(u) }

[<AbstractClass>]
type FSharpFunc<'T,'U,'V,'W>() =
inherit FSharpFunc<'T,('U -> 'V -> 'W)>()
abstract Invoke : 'T * 'U * 'V -> 'W
override f.Invoke(t) = (fun u v -> f.Invoke(t,u,v))
static member Adapt(f : 'T -> 'U -> 'V -> 'W) =
match box f with
static member Adapt(func : 'T -> 'U -> 'V -> 'W) =
match box func with
// Does it take three arguments without side effect?
| :? FSharpFunc<'T,'U,'V,'W> as f -> f

Expand All @@ -2805,7 +2805,7 @@ namespace Microsoft.FSharp.Core
member x.Invoke(t,u,v) = f.Invoke(t,u).Invoke(v) }

| _ -> { new FSharpFunc<'T,'U,'V,'W>() with
member x.Invoke(t,u,v) = (retype f : FSharpFunc<'T,('U -> 'V -> 'W)>).Invoke(t) u v }
member x.Invoke(t,u,v) = (retype func : FSharpFunc<'T,('U -> 'V -> 'W)>).Invoke(t) u v }

[<AbstractClass>]
type FSharpFunc<'T,'U,'V,'W,'X>() =
Expand Down
5 changes: 5 additions & 0 deletions src/fsharp/SignatureConformance.fs
Expand Up @@ -249,6 +249,11 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) =
let res =
(implArgInfos,sigArgInfos) ||> List.forall2 (List.forall2 (fun implArgInfo sigArgInfo ->
checkAttribs aenv implArgInfo.Attribs sigArgInfo.Attribs (fun attribs ->
match implArgInfo.Name, sigArgInfo.Name with
| Some iname, Some sname when sname.idText <> iname.idText ->
warning(Error (FSComp.SR.ArgumentsInSigAndImplMismatch(sname.idText, iname.idText),iname.idRange))
| _ -> ()

implArgInfo.Name <- sigArgInfo.Name
implArgInfo.Attribs <- attribs))) &&

Expand Down
7 changes: 7 additions & 0 deletions tests/fsharp/tests.fs
Expand Up @@ -2142,6 +2142,12 @@ module TypecheckTests =
[<Test>]
let ``type check neg99`` () = singleNegTest (testConfig "typecheck/sigs") "neg99"

[<Test>]
let ``type check neg100`` () =
let cfg = testConfig "typecheck/sigs"
let cfg = { cfg with fsc_flags = cfg.fsc_flags + " --warnon:3218" }
singleNegTest cfg "neg100"

[<Test>]
let ``type check neg_byref_1`` () = singleNegTest (testConfig "typecheck/sigs") "neg_byref_1"

Expand Down Expand Up @@ -2208,6 +2214,7 @@ module TypecheckTests =
[<Test>]
let ``type check neg_byref_23`` () = singleNegTest (testConfig "typecheck/sigs") "neg_byref_23"


module FscTests =
[<Test>]
let ``should be raised if AssemblyInformationalVersion has invalid version`` () =
Expand Down
18 changes: 18 additions & 0 deletions tests/fsharp/typecheck/sigs/neg100.bsl
@@ -0,0 +1,18 @@

neg100.fs(9,12,9,16): typecheck error FS3218: The argument names in the signature 'xxxx' and implementation 'qqqq' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(8,12,8,16): typecheck error FS3218: The argument names in the signature 'xxx' and implementation 'qqqq' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(7,12,7,16): typecheck error FS3218: The argument names in the signature 'xx' and implementation 'qqqq' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(6,12,6,16): typecheck error FS3218: The argument names in the signature 'x' and implementation 'qqqq' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(15,17,15,22): typecheck error FS3218: The argument names in the signature 'xxxx' and implementation 'qqqq3' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(14,17,14,22): typecheck error FS3218: The argument names in the signature 'xxx' and implementation 'qqqq3' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(13,17,13,22): typecheck error FS3218: The argument names in the signature 'xx' and implementation 'qqqq3' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(12,17,12,22): typecheck error FS3218: The argument names in the signature 'x' and implementation 'qqqq3' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.

neg100.fs(11,9,11,14): typecheck error FS3218: The argument names in the signature 'aaa' and implementation 'qqqq2' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.
16 changes: 16 additions & 0 deletions tests/fsharp/typecheck/sigs/neg100.fs
@@ -0,0 +1,16 @@


module M

let ffff ok = ok + 3
let gggg1 (qqqq : int) = qqqq + 3
let gggg2 (qqqq : int) = qqqq + 3
let gggg3 (qqqq : int) = qqqq + 3
let gggg4 (qqqq : int) = qqqq + 3

type C( qqqq2: int) =
member __.M1 (qqqq3: int) = qqqq2 + qqqq3 |> ignore
member __.M2 (qqqq3: int) = qqqq2 + qqqq3 |> ignore
member __.M3 (qqqq3: int) = qqqq2 + qqqq3 |> ignore
member __.M4 (qqqq3: int) = qqqq2 + qqqq3 |> ignore

16 changes: 16 additions & 0 deletions tests/fsharp/typecheck/sigs/neg100.fsi
@@ -0,0 +1,16 @@


module M

val ffff : int -> int
val gggg1 : x: int -> int
val gggg2 : xx: int -> int
val gggg3 : xxx: int -> int
val gggg4 : xxxx: int -> int

type C =
new : aaa : int -> C
member M1 : x:int -> unit
member M2 : xx:int -> unit
member M3 : xxx:int -> unit
member M4 : xxxx:int -> unit
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open System.Collections.Immutable
Expand Down
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
Expand Down
21 changes: 5 additions & 16 deletions vsintegration/src/FSharp.Editor/CodeFix/FixIndexerAccess.fs
@@ -1,34 +1,22 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
open System.Collections.Immutable
open System.Threading
open System.Threading.Tasks

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.CodeFixes
open Microsoft.CodeAnalysis.CodeActions
open SymbolHelpers

[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "FixIndexerAccess"); Shared>]
type internal FSharpFixIndexerAccessCodeFixProvider() =
inherit CodeFixProvider()
let fixableDiagnosticIds = set ["FS3217"]

let createCodeFix (title: string, context: CodeFixContext, textChange: TextChange) =
CodeAction.Create(
title,
(fun (cancellationToken: CancellationToken) ->
async {
let! cancellationToken = Async.CancellationToken
let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask
return context.Document.WithText(sourceText.WithChanges(textChange))
} |> RoslynHelpers.StartAsyncAsTask(cancellationToken)),
title)

override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

override __.RegisterCodeFixesAsync context : Task =
Expand Down Expand Up @@ -60,9 +48,10 @@ type internal FSharpFixIndexerAccessCodeFixProvider() =
| _ -> context.Span,sourceText.GetSubText(context.Span).ToString()

let codefix =
createCodeFix(
createTextChangeCodeFix(
FSComp.SR.addIndexerDot(),
context,
TextChange(span, replacement.TrimEnd() + "."))
(fun () -> asyncMaybe.Return [| TextChange(span, replacement.TrimEnd() + ".") |]))

context.RegisterCodeFix(codefix, diagnostics))
} |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
Expand Down
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
Expand Down
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open System.Threading.Tasks
Expand Down
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open System.Threading
Expand Down
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Collections.Immutable
open System.Composition
open System.Threading.Tasks

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.CodeFixes

open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
open Microsoft.VisualStudio.FSharp.Editor.SymbolHelpers
open Microsoft.FSharp.Compiler.SourceCodeServices.Keywords

[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "FSharpRenameParamToMatchSignature"); Shared>]
type internal FSharpRenameParamToMatchSignature
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

inherit CodeFixProvider()
static let userOpName = "RenameParamToMatchSignature"
let fixableDiagnosticIds = ["FS3218"]


override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

override __.RegisterCodeFixesAsync context : Task =
asyncMaybe {
match context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) |> Seq.toList with
| [diagnostic] ->
let message = diagnostic.GetMessage()
let parts = System.Text.RegularExpressions.Regex.Match(message, ".+'(.+)'.+'(.+)'.+")
if parts.Success then

let diagnostics = ImmutableArray.Create diagnostic
let suggestion = parts.Groups.[1].Value
let replacement = QuoteIdentifierIfNeeded suggestion
let computeChanges() =
asyncMaybe {
let document = context.Document
let! cancellationToken = Async.CancellationToken |> liftAsync
let! sourceText = document.GetTextAsync(cancellationToken)
let! symbolUses = getSymbolUsesOfSymbolAtLocationInDocument (document, context.Span.Start, projectInfoManager, checkerProvider.Checker, userOpName)
let changes =
[| for symbolUse in symbolUses do
match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) with
| None -> ()
| Some span ->
let textSpan = Tokenizer.fixupSpan(sourceText, span)
yield TextChange(textSpan, replacement) |]
return changes
}
let title = FSComp.SR.replaceWithSuggestion suggestion
let codefix = createTextChangeCodeFix(title, context, computeChanges)
context.RegisterCodeFix(codefix, diagnostics)
| _ -> ()
} |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace rec Microsoft.VisualStudio.FSharp.Editor
namespace Microsoft.VisualStudio.FSharp.Editor

open System
open System.Composition
Expand Down

0 comments on commit 0f259ed

Please sign in to comment.