diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs
index 9df233a9388..f18525f47f2 100644
--- a/src/Compiler/Checking/CheckExpressions.fs
+++ b/src/Compiler/Checking/CheckExpressions.fs
@@ -8445,7 +8445,19 @@ and TcUnionCaseOrExnCaseOrActivePatternResultItemThen (cenv: cenv) overallTy env
// This is where the constructor expects arguments but is not applied to arguments, hence build a lambda
numArgTys,
(fun () ->
- let vs, args = argTys |> List.mapi (fun i ty -> mkCompGenLocal mItem ("arg" + string i) ty) |> List.unzip
+ let argNamesIfFeatureEnabled =
+ if g.langVersion.SupportsFeature LanguageFeature.ImprovedImpliedArgumentNames then
+ argNames
+ else
+ []
+
+ let vs, args =
+ argTys
+ |> List.mapi (fun i ty ->
+ let argName = argNamesIfFeatureEnabled |> List.tryItem i |> Option.map (fun x -> x.idText) |> Option.defaultWith (fun () -> "arg" + string i)
+ mkCompGenLocal mItem argName ty)
+ |> List.unzip
+
let constrApp = mkConstrApp mItem args
let lam = mkMultiLambda mItem vs (constrApp, tyOfExpr g constrApp)
lam)
@@ -9535,7 +9547,13 @@ and TcMethodApplication_CheckArguments
let denv = env.DisplayEnv
match curriedCallerArgsOpt with
| None ->
- let curriedArgTys, returnTy =
+ let curriedArgTys, curriedArgNamesIfFeatureEnabled, returnTy =
+ let paramNamesIfFeatureEnabled (g: TcGlobals) (meth: MethInfo) =
+ if g.langVersion.SupportsFeature LanguageFeature.ImprovedImpliedArgumentNames then
+ meth.GetParamNames()
+ else
+ []
+
match candidates with
// "single named item" rule. This is where we have a single accessible method
// member x.M(arg1, ..., argN)
@@ -9547,19 +9565,23 @@ and TcMethodApplication_CheckArguments
// to their default values (for optionals) and be part of the return tuple (for out args).
| [calledMeth] ->
let curriedArgTys, returnTy = UnifyMatchingSimpleArgumentTypes cenv env exprTy.Commit calledMeth mMethExpr mItem
- curriedArgTys, MustEqual returnTy
+ curriedArgTys, paramNamesIfFeatureEnabled g calledMeth, MustEqual returnTy
| _ ->
let domainTy, returnTy = UnifyFunctionType None cenv denv mMethExpr exprTy.Commit
let argTys = if isUnitTy g domainTy then [] else tryDestRefTupleTy g domainTy
// Only apply this rule if a candidate method exists with this number of arguments
- let argTys =
- if candidates |> List.exists (CalledMethHasSingleArgumentGroupOfThisLength argTys.Length) then
- argTys
- else
- [domainTy]
- [argTys], MustEqual returnTy
-
- let lambdaVarsAndExprs = curriedArgTys |> List.mapiSquared (fun i j ty -> mkCompGenLocal mMethExpr ("arg"+string i+string j) ty)
+ let argTys, argNames =
+ match candidates |> List.tryFind (CalledMethHasSingleArgumentGroupOfThisLength argTys.Length) with
+ | Some meth -> argTys, paramNamesIfFeatureEnabled g meth
+ | None -> [domainTy], [[None]]
+ [argTys], argNames, MustEqual returnTy
+
+ let lambdaVarsAndExprs =
+ curriedArgTys
+ |> List.mapiSquared (fun i j ty ->
+ let argName = curriedArgNamesIfFeatureEnabled |> List.tryItem i |> Option.bind (List.tryItem j) |> Option.flatten |> Option.defaultWith (fun () -> "arg" + string i + string j)
+ mkCompGenLocal mMethExpr argName ty)
+
let unnamedCurriedCallerArgs = lambdaVarsAndExprs |> List.mapSquared (fun (_, e) -> CallerArg(tyOfExpr g e, e.Range, false, e))
let namedCurriedCallerArgs = lambdaVarsAndExprs |> List.map (fun _ -> [])
let lambdaVars = List.mapSquared fst lambdaVarsAndExprs
diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs
index ceb13545da8..67b3a61fd5c 100644
--- a/src/Compiler/Checking/MethodCalls.fs
+++ b/src/Compiler/Checking/MethodCalls.fs
@@ -1266,8 +1266,23 @@ let BuildNewDelegateExpr (eventInfoOpt: EventInfo option, g, amap, delegateTy, d
if List.exists (isByrefTy g) delArgTys then
error(Error(FSComp.SR.tcFunctionRequiresExplicitLambda(delArgTys.Length), m))
+ let delFuncArgNamesIfFeatureEnabled =
+ match delFuncExpr with
+ | Expr.Val (valRef = vref) when g.langVersion.SupportsFeature LanguageFeature.ImprovedImpliedArgumentNames ->
+ match vref.ValReprInfo with
+ | Some repr when repr.ArgNames.Length = delArgTys.Length -> Some repr.ArgNames
+ | _ -> None
+ | _ -> None
+
let delArgVals =
- delArgTys |> List.mapi (fun i argTy -> fst (mkCompGenLocal m ("delegateArg" + string i) argTy))
+ delArgTys
+ |> List.mapi (fun i argTy ->
+ let argName =
+ match delFuncArgNamesIfFeatureEnabled with
+ | Some argNames -> argNames[i]
+ | None -> "delegateArg" + string i
+
+ fst (mkCompGenLocal m argName argTy))
let expr =
let args =
diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs
index 95da6fa9bfd..572dfa451cc 100644
--- a/src/Compiler/Checking/NicePrint.fs
+++ b/src/Compiler/Checking/NicePrint.fs
@@ -168,7 +168,19 @@ module internal PrintUtilities =
| GenericParameterStyle.Prefix -> true
| GenericParameterStyle.Suffix -> false
- let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) =
+ ///
+ /// Creates a layout for TyconRef.
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Used in the case the TyconRef is a nested type from another assembly which has generic type parameters in the path.
+ /// For example: System.Collections.Immutable.ImmutableArray>'T<.Builder
+ /// Lead to access path: System.Collections.Immutable.ImmutableArray`1
+ /// ImmutableArray`1 will be transformed to ImmutableArray>'t<
+ ///
+ let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) (demangledPath: string list option) =
let prefix = usePrefix denv tcref
let isArray = not prefix && isArrayTyconRef denv.g tcref
@@ -201,21 +213,22 @@ module internal PrintUtilities =
if denv.shortTypeNames then
tyconTextL
else
- let path = tcref.CompilationPath.DemangledPath
let path =
if denv.includeStaticParametersInTypeNames then
- path
+ Option.defaultValue tcref.CompilationPath.DemangledPath demangledPath
else
- path |> List.map (fun s ->
+ tcref.CompilationPath.DemangledPath
+ |> List.map (fun s ->
let i = s.IndexOf(',')
if i <> -1 then s.Substring(0, i)+"<...>" // apparently has static params, shorten
else s)
+
let pathText = trimPathByDisplayEnv denv path
if pathText = "" then tyconTextL else leftL (tagUnknownEntity pathText) ^^ tyconTextL
let layoutBuiltinAttribute (denv: DisplayEnv) (attrib: BuiltinAttribInfo) =
let tcref = attrib.TyconRef
- squareAngleL (layoutTyconRefImpl true denv tcref)
+ squareAngleL (layoutTyconRefImpl true denv tcref None)
/// layout the xml docs immediately before another block
let layoutXmlDoc (denv: DisplayEnv) alwaysAddEmptyLine (xml: XmlDoc) restL =
@@ -499,7 +512,7 @@ module PrintTypes =
layoutAccessibilityCore denv accessibility ++ itemL
/// Layout a reference to a type
- let layoutTyconRef denv tcref = layoutTyconRefImpl false denv tcref
+ let layoutTyconRef denv tcref = layoutTyconRefImpl false denv tcref None
/// Layout the flags of a member
let layoutMemberFlags (memFlags: SynMemberFlags) =
@@ -571,7 +584,7 @@ module PrintTypes =
/// Layout an attribute 'Type(arg1, ..., argN)'
and layoutAttrib denv (Attrib(tcref, _, args, props, _, _, _)) =
- let tcrefL = layoutTyconRefImpl true denv tcref
+ let tcrefL = layoutTyconRefImpl true denv tcref None
let argsL = bracketL (layoutAttribArgs denv args props)
if List.isEmpty args && List.isEmpty props then
tcrefL
@@ -900,7 +913,39 @@ module PrintTypes =
| TType_ucase (UnionCaseRef(tc, _), args)
| TType_app (tc, args, _) ->
let prefix = usePrefix denv tc
- layoutTypeAppWithInfoAndPrec denv env (layoutTyconRef denv tc) prec prefix args
+ let demangledCompilationPathOpt, args =
+ if not denv.includeStaticParametersInTypeNames then
+ None, args
+ else
+ let regex = System.Text.RegularExpressions.Regex(@"\`\d+")
+ let path, skip =
+ (0, tc.CompilationPath.DemangledPath)
+ ||> List.mapFold (fun skip path ->
+ // Verify the path does not contain a generic parameter count.
+ // For example Foo`3 indicates that there are three parameters in args that belong to this path.
+ let m = regex.Match(path)
+ if not m.Success then
+ path, skip
+ else
+ let take = m.Value.Replace("`", "") |> int
+ let genericArgs =
+ List.skip skip args
+ |> List.take take
+ |> List.map (layoutTypeWithInfoAndPrec denv env prec >> showL)
+ |> String.concat ","
+ |> sprintf "<%s>"
+ String.Concat(path.Substring(0, m.Index), genericArgs), (skip + take)
+ )
+
+ Some path, List.skip skip args
+
+ layoutTypeAppWithInfoAndPrec
+ denv
+ env
+ (layoutTyconRefImpl false denv tc demangledCompilationPathOpt)
+ prec
+ prefix
+ args
// Layout a tuple type
| TType_anon (anonInfo, tys) ->
@@ -1621,7 +1666,7 @@ module TastDefinitionPrinting =
let layoutExtensionMember denv infoReader (vref: ValRef) =
let (@@*) = if denv.printVerboseSignatures then (@@----) else (@@--)
let tycon = vref.MemberApparentEntity.Deref
- let nameL = layoutTyconRefImpl false denv vref.MemberApparentEntity
+ let nameL = layoutTyconRefImpl false denv vref.MemberApparentEntity None
let nameL = layoutAccessibility denv tycon.Accessibility nameL // "type-accessibility"
let tps =
match PartitionValTyparsForApparentEnclosingType denv.g vref.Deref with
@@ -2615,7 +2660,7 @@ let stringOfFSAttrib denv x = x |> PrintTypes.layoutAttrib denv |> squareAngleL
let stringOfILAttrib denv x = x |> PrintTypes.layoutILAttrib denv |> squareAngleL |> showL
-let fqnOfEntityRef g x = x |> layoutTyconRefImpl false (DisplayEnv.Empty g) |> showL
+let fqnOfEntityRef g x = layoutTyconRefImpl false (DisplayEnv.Empty g) x None |> showL
let layoutImpliedSignatureOfModuleOrNamespace showHeader denv infoReader ad m contents =
InferredSigPrinting.layoutImpliedSignatureOfModuleOrNamespace showHeader denv infoReader ad m contents
diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs
index 2fe6031aeae..4548b98eb39 100644
--- a/src/Compiler/Checking/infos.fs
+++ b/src/Compiler/Checking/infos.fs
@@ -1100,6 +1100,21 @@ type MethInfo =
member x.GetFSharpReturnType(amap, m, minst) =
x.GetCompiledReturnType(amap, m, minst) |> GetFSharpViewOfReturnType amap.g
+ member x.GetParamNames() =
+ match x with
+ | FSMeth (g, _, vref, _) ->
+ ParamNameAndType.FromMember x.IsCSharpStyleExtensionMember g vref |> List.mapSquared (fun (ParamNameAndType (name, _)) -> name |> Option.map (fun x -> x.idText))
+ | ILMeth (ilMethInfo = ilminfo) ->
+ // A single group of tupled arguments
+ [ ilminfo.ParamMetadata |> List.map (fun x -> x.Name) ]
+#if !NO_TYPEPROVIDERS
+ | ProvidedMeth (_, mi, _, m) ->
+ // A single group of tupled arguments
+ [ [ for p in mi.PApplyArray((fun mi -> mi.GetParameters()), "GetParameters", m) do
+ yield p.PUntaint((fun p -> Some p.Name), m) ] ]
+#endif
+ | _ -> []
+
/// Get the parameter types of a method info
member x.GetParamTypes(amap, m, minst) =
match x with
diff --git a/src/Compiler/Checking/infos.fsi b/src/Compiler/Checking/infos.fsi
index 550c7860b34..eb34d3523cb 100644
--- a/src/Compiler/Checking/infos.fsi
+++ b/src/Compiler/Checking/infos.fsi
@@ -517,6 +517,9 @@ type MethInfo =
/// Get the ParamData objects for the parameters of a MethInfo
member GetParamDatas: amap: ImportMap * m: range * minst: TType list -> ParamData list list
+ /// Get the parameter names of a MethInfo
+ member GetParamNames: unit -> string option list list
+
/// Get the parameter types of a method info
member GetParamTypes: amap: ImportMap * m: range * minst: TType list -> TType list list
diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt
index cd463878354..43a4b71349b 100644
--- a/src/Compiler/FSComp.txt
+++ b/src/Compiler/FSComp.txt
@@ -1573,6 +1573,7 @@ featureNonInlineLiteralsAsPrintfFormat,"String values marked as literals and IL
featureNestedCopyAndUpdate,"Nested record field copy-and-update"
featureExtendedStringInterpolation,"Extended string interpolation similar to C# raw string literals."
featureWarningWhenMultipleRecdTypeChoice,"Raises warnings when multiple record type matches were found during name resolution because of overlapping field names."
+featureImprovedImpliedArgumentNames,"Improved implied argument names"
3353,fsiInvalidDirective,"Invalid directive '#%s %s'"
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs
index 996b63760e8..f85f7833e16 100644
--- a/src/Compiler/Facilities/LanguageFeatures.fs
+++ b/src/Compiler/Facilities/LanguageFeatures.fs
@@ -68,6 +68,7 @@ type LanguageFeature =
| NestedCopyAndUpdate
| ExtendedStringInterpolation
| WarningWhenMultipleRecdTypeChoice
+ | ImprovedImpliedArgumentNames
/// LanguageVersion management
type LanguageVersion(versionText) =
@@ -159,6 +160,7 @@ type LanguageVersion(versionText) =
LanguageFeature.NestedCopyAndUpdate, previewVersion
LanguageFeature.ExtendedStringInterpolation, previewVersion
LanguageFeature.WarningWhenMultipleRecdTypeChoice, previewVersion
+ LanguageFeature.ImprovedImpliedArgumentNames, previewVersion
]
@@ -282,6 +284,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.NestedCopyAndUpdate -> FSComp.SR.featureNestedCopyAndUpdate ()
| LanguageFeature.ExtendedStringInterpolation -> FSComp.SR.featureExtendedStringInterpolation ()
| LanguageFeature.WarningWhenMultipleRecdTypeChoice -> FSComp.SR.featureWarningWhenMultipleRecdTypeChoice ()
+ | LanguageFeature.ImprovedImpliedArgumentNames -> FSComp.SR.featureImprovedImpliedArgumentNames ()
/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi
index ab85fdc4aae..dcc0c660f2c 100644
--- a/src/Compiler/Facilities/LanguageFeatures.fsi
+++ b/src/Compiler/Facilities/LanguageFeatures.fsi
@@ -58,6 +58,7 @@ type LanguageFeature =
| NestedCopyAndUpdate
| ExtendedStringInterpolation
| WarningWhenMultipleRecdTypeChoice
+ | ImprovedImpliedArgumentNames
/// LanguageVersion management
type LanguageVersion =
diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs
index 702f7292429..1e15447646c 100644
--- a/src/Compiler/TypedTree/TypedTreeOps.fs
+++ b/src/Compiler/TypedTree/TypedTreeOps.fs
@@ -3117,7 +3117,8 @@ type DisplayEnv =
suppressInlineKeyword = false
showDocumentation = true
shrinkOverloads = false
- escapeKeywordNames = true }
+ escapeKeywordNames = true
+ includeStaticParametersInTypeNames = true }
denv.SetOpenPaths
[ FSharpLib.RootPath
FSharpLib.CorePath
diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf
index 69d868814d0..0463f932964 100644
--- a/src/Compiler/xlf/FSComp.txt.cs.xlf
+++ b/src/Compiler/xlf/FSComp.txt.cs.xlf
@@ -272,6 +272,11 @@
implicitní yield
+
+
+ Improved implied argument names
+
+
Notace expr[idx] pro indexování a vytváření řezů
diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf
index e372842d253..ee5d2c85eee 100644
--- a/src/Compiler/xlf/FSComp.txt.de.xlf
+++ b/src/Compiler/xlf/FSComp.txt.de.xlf
@@ -272,6 +272,11 @@
implizite yield-Anweisung
+
+
+ Improved implied argument names
+
+
expr[idx]-Notation zum Indizieren und Aufteilen
diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf
index 480813c8e22..f4782ec0fb9 100644
--- a/src/Compiler/xlf/FSComp.txt.es.xlf
+++ b/src/Compiler/xlf/FSComp.txt.es.xlf
@@ -272,6 +272,11 @@
elemento yield implícito
+
+
+ Improved implied argument names
+
+
Notación para indexación y segmentación expr[idx]
diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf
index 6c3ee073570..d2582301f83 100644
--- a/src/Compiler/xlf/FSComp.txt.fr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.fr.xlf
@@ -272,6 +272,11 @@
yield implicite
+
+
+ Improved implied argument names
+
+
Notation expr[idx] pour l’indexation et le découpage
diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf
index e47a248b9dc..2317547d1af 100644
--- a/src/Compiler/xlf/FSComp.txt.it.xlf
+++ b/src/Compiler/xlf/FSComp.txt.it.xlf
@@ -272,6 +272,11 @@
istruzione yield implicita
+
+
+ Improved implied argument names
+
+
Notazione expr[idx] per l'indicizzazione e il sezionamento
diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf
index 22a3b97a621..cc4ef5a416f 100644
--- a/src/Compiler/xlf/FSComp.txt.ja.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ja.xlf
@@ -272,6 +272,11 @@
暗黙的な yield
+
+
+ Improved implied argument names
+
+
インデックス作成とスライス用の expr[idx] 表記
diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf
index e99b84363e2..b2654ea3d53 100644
--- a/src/Compiler/xlf/FSComp.txt.ko.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ko.xlf
@@ -272,6 +272,11 @@
암시적 yield
+
+
+ Improved implied argument names
+
+
인덱싱 및 슬라이싱을 위한 expr[idx] 표기법
diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf
index 94f6c4edfce..099c990c934 100644
--- a/src/Compiler/xlf/FSComp.txt.pl.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pl.xlf
@@ -272,6 +272,11 @@
niejawne słowo kluczowe yield
+
+
+ Improved implied argument names
+
+
notacja wyrażenia expr[idx] do indeksowania i fragmentowania
diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
index 37390cf9ef9..1af65d00b33 100644
--- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
@@ -272,6 +272,11 @@
yield implícito
+
+
+ Improved implied argument names
+
+
notação expr[idx] para indexação e fatia
diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf
index 5ed87a2747f..eef33789409 100644
--- a/src/Compiler/xlf/FSComp.txt.ru.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ru.xlf
@@ -272,6 +272,11 @@
неявное использование yield
+
+
+ Improved implied argument names
+
+
expr[idx] для индексации и среза
diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf
index bc9a8d090e4..4700c44f307 100644
--- a/src/Compiler/xlf/FSComp.txt.tr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.tr.xlf
@@ -272,6 +272,11 @@
örtük yield
+
+
+ Improved implied argument names
+
+
Dizin oluşturma ve dilimleme için expr[idx] gösterimi
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
index eb467ba660f..2c88f9fe9c8 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
@@ -272,6 +272,11 @@
隐式 yield
+
+
+ Improved implied argument names
+
+
用于索引和切片的 expr[idx] 表示法
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
index 1e96abf59d5..82f98673bf3 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
@@ -272,6 +272,11 @@
隱含 yield
+
+
+ Improved implied argument names
+
+
用於編製索引和分割的 expr[idx] 註釋
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/ArgumentNames.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/ArgumentNames.fs
new file mode 100644
index 00000000000..f23179b7fa8
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/ArgumentNames.fs
@@ -0,0 +1,178 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Compiler.ComponentTests.EmittedIL
+
+open Xunit
+open FSharp.Test.Compiler
+
+module ArgumentNames =
+
+ []
+ let ``Implied argument names are taken from method or constructor``() =
+ FSharp """
+module ArgumentNames
+
+type M (name: string, count: int) =
+ []
+ static member Open (fileName: string) = ()
+
+let test1 = M
+let test2 = M.Open
+ """
+ |> withLangVersionPreview
+ |> compile
+ |> shouldSucceed
+ |> verifyIL ["""
+.method public static class ArgumentNames/M
+ test1(string name,
+ int32 count) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: newobj instance void ArgumentNames/M::.ctor(string,
+ int32)
+ IL_0007: ret
+}
+
+.method public static void test2(string fileName) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call void ArgumentNames/M::Open(string)
+ IL_0006: ret
+} """ ]
+
+ []
+ let ``Implied argument names are taken from curried method``() =
+ FSharp """
+module ArgumentNames
+
+type M =
+ []
+ static member Write (fileName: string, offset: int) (data: byte[]) = ()
+
+let test1 = M.Write
+ """
+ |> withLangVersionPreview
+ |> compile
+ |> shouldSucceed
+ |> verifyIL ["""
+.method public static void test1(string fileName,
+ int32 offset,
+ uint8[] data) cil managed
+{
+ .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 02 00 00 00 01 00 00 00 00 00 )
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: ldarg.2
+ IL_0003: call void ArgumentNames/M::Write(string,
+ int32,
+ uint8[])
+ IL_0008: ret
+} """ ]
+
+ []
+ let ``Implied argument names are taken from C#-style extension method``() =
+ FSharp """
+module ArgumentNames
+
+open System.Runtime.CompilerServices
+
+[]
+type Ext =
+ []
+ []
+ static member Print(x: int, yy: string) = printfn "%d%s" x yy
+
+let test1 = (3).Print
+ """
+ |> withLangVersionPreview
+ |> compile
+ |> shouldSucceed
+ |> verifyIL ["""
+ Invoke(string yy) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldc.i4.3
+ IL_0001: ldarg.1
+ IL_0002: call void ArgumentNames/Ext::Print(int32,
+ string)
+ IL_0007: ldnull
+ IL_0008: ret
+} """ ]
+
+ []
+ let ``Implied argument names are taken from DU case constructor or exception``() =
+ FSharp """
+module ArgumentNames
+
+exception X of code: int * string
+
+type DU =
+ | Case1 of code: int * string
+
+let test1 = X
+let test2 = Case1
+ """
+ |> withLangVersionPreview
+ |> compile
+ |> shouldSucceed
+ |> verifyIL ["""
+.method public static class [runtime]System.Exception
+ test1(int32 code,
+ string Data1) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: newobj instance void ArgumentNames/X::.ctor(int32,
+ string)
+ IL_0007: ret
+}
+
+.method public static class ArgumentNames/DU
+ test2(int32 code,
+ string Item2) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: call class ArgumentNames/DU ArgumentNames/DU::NewCase1(int32,
+ string)
+ IL_0007: ret
+} """ ]
+
+ []
+ let ``Implied argument names are taken from function and used in delegate Invoke``() =
+ FSharp """
+module ArgumentNames
+
+[]
+let add num1 num2 = printfn "%d" (num1 + num2)
+
+let test1 = System.Action<_, _>(add)
+ """
+ |> withLangVersionPreview
+ |> compile
+ |> shouldSucceed
+ |> verifyIL ["""
+.method assembly static void Invoke(int32 num1,
+ int32 num2) cil managed
+{
+
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: tail.
+ IL_0004: call void ArgumentNames::'add'(int32,
+ int32)
+ IL_0009: ret
+} """ ]
diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
index 1ac6062d5bf..85aec6c485e 100644
--- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
+++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
@@ -113,6 +113,7 @@
+
@@ -247,6 +248,7 @@
+
diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/NestedTypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/NestedTypeTests.fs
new file mode 100644
index 00000000000..9fb4ee4360c
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Signatures/NestedTypeTests.fs
@@ -0,0 +1,99 @@
+module FSharp.Compiler.ComponentTests.Signatures.NestedTypeTests
+
+open Xunit
+open FsUnit
+open FSharp.Test
+open FSharp.Test.Compiler
+open FSharp.Compiler.ComponentTests.Signatures.TestHelpers
+
+[]
+let ``Nested type with generics`` () =
+ let CSLib =
+ CSharp """
+namespace Lib
+{
+ public class Upper
+ {
+ public class Lower
+ {
+ public void Meh()
+ {
+
+ }
+ }
+ }
+}
+"""
+
+ FSharp
+ """
+module Sample
+
+open Lib
+
+let f (g: Upper.Lower) = g.Meh()
+"""
+ |> withReferences [ CSLib ]
+ |> printSignatures
+ |> should equal
+ """
+module Sample
+
+val f: g: Lib.Upper.Lower -> unit"""
+
+[]
+let ``Multiple generics in nested type`` () =
+ let CSLib =
+ CSharp """
+namespace Lib
+{
+ public class Root
+ {
+ public class Foo
+ {
+ public class Bar
+ {
+ public void Meh()
+ {
+
+ }
+ }
+ }
+ }
+}
+"""
+
+ FSharp
+ """
+module Sample
+
+open System
+open Lib
+
+let f (g: Root.Foo.Bar) = g.Meh()
+"""
+ |> withReferences [ CSLib ]
+ |> printSignatures
+ |> should equal
+ """
+module Sample
+
+val f: g: Lib.Root.Foo.Bar -> unit"""
+
+[]
+let ``ImmutableArray<'T>.Builder roundtrip`` () =
+ let impl =
+ """
+module Library
+
+open System.Collections.Immutable
+
+type ImmutableArrayViaBuilder<'T>(builder: ImmutableArray<'T>.Builder) = class end
+"""
+
+ let signature = printSignatures (Fs impl)
+
+ Fsi signature
+ |> withAdditionalSourceFile (FsSource impl)
+ |> compile
+ |> shouldSucceed
diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs
index 59cb4faac1a..db50005068c 100644
--- a/tests/FSharp.Test.Utilities/Compiler.fs
+++ b/tests/FSharp.Test.Utilities/Compiler.fs
@@ -891,7 +891,29 @@ module rec Compiler =
| FS fsSource ->
let source = fsSource.Source.GetSourceText |> Option.defaultValue ""
let fileName = fsSource.Source.ChangeExtension.GetSourceFileName
- let options = fsSource.Options |> Array.ofList
+
+ let references =
+ let disposals = ResizeArray()
+ let outputDirectory =
+ match fsSource.OutputDirectory with
+ | Some di -> di
+ | None -> DirectoryInfo(tryCreateTemporaryDirectory())
+ let references = processReferences fsSource.References outputDirectory
+ if references.IsEmpty then
+ Array.empty
+ else
+ outputDirectory.Create()
+ disposals.Add({ new IDisposable with member _.Dispose() = outputDirectory.Delete(true) })
+ // Note that only the references are relevant here
+ let compilation = Compilation.Compilation([], CompileOutput.Exe,Array.empty, TargetFramework.Current, references, None, None)
+ evaluateReferences outputDirectory disposals fsSource.IgnoreWarnings compilation
+ |> fst
+
+ let options =
+ [|
+ yield! fsSource.Options |> Array.ofList
+ yield! references
+ |]
CompilerAssert.TypeCheck(options, fileName, source)
| _ -> failwith "Typecheck only supports F#"
diff --git a/tests/service/ExprTests.fs b/tests/service/ExprTests.fs
index b2679c8863c..8c8128eca18 100644
--- a/tests/service/ExprTests.fs
+++ b/tests/service/ExprTests.fs
@@ -661,7 +661,9 @@ let testMutableVar = mutableVar 1
let testMutableConst = mutableConst ()
"""
- let createOptions() = createOptionsAux [fileSource1; fileSource2] []
+ let createOptionsWithArgs args = createOptionsAux [ fileSource1; fileSource2 ] args
+
+ let createOptions() = createOptionsWithArgs []
let operatorTests = """
module OperatorTests{0}
@@ -732,7 +734,7 @@ let ignoreTestIfStackOverflowExpected () =
/// This test is run in unison with its optimized counterpart below
[]
let ``Test Unoptimized Declarations Project1`` () =
- let cleanup, options = Project1.createOptions()
+ let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ]
use _holder = cleanup
let exprChecker = FSharpChecker.Create(keepAssemblyContents=true)
let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate
@@ -796,7 +798,7 @@ let ``Test Unoptimized Declarations Project1`` () =
"member .ctor(c,d) = (new Object(); ()) @ (105,5--105,20)";
"member Method(x) (a,b) = 1 @ (106,37--106,38)";
"member CurriedMethod(x) (a1,b1) (a2,b2) = 1 @ (107,63--107,64)";
- "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),m.Method(7,8),fun tupledArg -> let arg00: Microsoft.FSharp.Core.int = tupledArg.Item0 in let arg01: Microsoft.FSharp.Core.int = tupledArg.Item1 in fun tupledArg -> let arg10: Microsoft.FSharp.Core.int = tupledArg.Item0 in let arg11: Microsoft.FSharp.Core.int = tupledArg.Item1 in m.CurriedMethod(arg00,arg01,arg10,arg11) (9,10) (11,12)) @ (110,8--110,9)";
+ "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),m.Method(7,8),fun tupledArg -> let a1: Microsoft.FSharp.Core.int = tupledArg.Item0 in let b1: Microsoft.FSharp.Core.int = tupledArg.Item1 in fun tupledArg -> let a2: Microsoft.FSharp.Core.int = tupledArg.Item0 in let b2: Microsoft.FSharp.Core.int = tupledArg.Item1 in m.CurriedMethod(a1,b1,a2,b2) (9,10) (11,12)) @ (110,8--110,9)";
"let testFunctionThatUsesUnitsOfMeasure(x) (y) = Operators.op_Addition,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (arg0_0,arg1_0),x,y) @ (122,70--122,75)";
"let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.array = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress, false, ILArrayShape [(Some 0, None)], !0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)";
"let testFunctionThatUsesStructs1(dt) = dt.AddDays(3) @ (139,57--139,72)";
@@ -867,7 +869,7 @@ let ``Test Unoptimized Declarations Project1`` () =
[]
let ``Test Optimized Declarations Project1`` () =
- let cleanup, options = Project1.createOptions()
+ let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ]
use _holder = cleanup
let exprChecker = FSharpChecker.Create(keepAssemblyContents=true)
let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate
@@ -931,7 +933,7 @@ let ``Test Optimized Declarations Project1`` () =
"member .ctor(c,d) = (new Object(); ()) @ (105,5--105,20)";
"member Method(x) (a,b) = 1 @ (106,37--106,38)";
"member CurriedMethod(x) (a1,b1) (a2,b2) = 1 @ (107,63--107,64)";
- "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),m.Method(7,8),let arg00: Microsoft.FSharp.Core.int = 9 in let arg01: Microsoft.FSharp.Core.int = 10 in let arg10: Microsoft.FSharp.Core.int = 11 in let arg11: Microsoft.FSharp.Core.int = 12 in m.CurriedMethod(arg00,arg01,arg10,arg11)) @ (110,8--110,9)";
+ "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),m.Method(7,8),let a1: Microsoft.FSharp.Core.int = 9 in let b1: Microsoft.FSharp.Core.int = 10 in let a2: Microsoft.FSharp.Core.int = 11 in let b2: Microsoft.FSharp.Core.int = 12 in m.CurriedMethod(a1,b1,a2,b2)) @ (110,8--110,9)";
"let testFunctionThatUsesUnitsOfMeasure(x) (y) = Operators.op_Addition,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (arg0_0,arg1_0),x,y) @ (122,70--122,75)";
"let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.array = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress, false, ILArrayShape [(Some 0, None)], !0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)";
"let testFunctionThatUsesStructs1(dt) = dt.AddDays(3) @ (139,57--139,72)";
diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs
index c257ffa0102..756ee0e4980 100644
--- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs
+++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs
@@ -30,7 +30,7 @@ module HintService =
|> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForMemberOrFunctionOrValue sourceText symbol)
| HintKind.ParameterNameHint, (:? FSharpUnionCase as symbol) ->
symbolUses
- |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase symbol)
+ |> Seq.collect (InlineParameterNameHints(parseResults).GetHintsForUnionCase sourceText symbol)
| _ -> []
hintKinds |> Set.toList |> List.map getHintsPerKind
diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs
index f428a144cfb..867377cc335 100644
--- a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs
+++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs
@@ -147,7 +147,7 @@ type InlineParameterNameHints(parseResults: FSharpParseFileResults) =
else
[]
- member _.GetHintsForUnionCase (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) =
+ member _.GetHintsForUnionCase (sourceText: SourceText) (symbol: FSharpUnionCase) (symbolUse: FSharpSymbolUse) =
if isUnionCaseValidForHint symbol symbolUse then
let fields = Seq.toList symbol.Fields
@@ -155,13 +155,17 @@ type InlineParameterNameHints(parseResults: FSharpParseFileResults) =
let ranges =
parseResults.GetAllArgumentsForFunctionApplicationAtPosition symbolUse.Range.Start
+ let argumentNames =
+ match ranges with
+ | Some ranges -> (List.map (getSourceTextAtRange sourceText)) ranges
+ | None -> []
// When not all field values are provided (as the user is typing), don't show anything yet
match ranges with
| Some ranges when ranges.Length = fields.Length ->
fields
- |> List.zip ranges
- |> List.where (snd >> fieldNameExists)
- |> List.map getFieldHint
+ |> List.zip3 argumentNames ranges
+ |> List.where (fun (argumentName, _, parameter) -> fieldNameExists parameter && argumentName <> parameter.DisplayName)
+ |> List.map (fun (_, range, parameter) -> getFieldHint (range, parameter))
| _ -> []
else
diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs
index 1607662a96c..c1cf532607a 100644
--- a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs
+++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs
@@ -347,6 +347,39 @@ let b = Rectangle (1, 2)
Assert.Equal(expected, actual)
+ []
+ let ``Hints are not shown for discriminated union case fields with the same names as arguements`` () =
+ let code =
+ """
+type Shape =
+ | Square of side: int
+ | Rectangle of width: int * height: int
+
+let width = 5
+let a = Square 1
+let b = Rectangle (width, 2)
+"""
+
+ let document = getFsDocument code
+
+ let expected =
+ [
+ {
+ Content = "side = "
+ Location = (6, 16)
+ Tooltip = "field side"
+ }
+ {
+ Content = "height = "
+ Location = (7, 27)
+ Tooltip = "field height"
+ }
+ ]
+
+ let actual = getParameterNameHints document
+
+ Assert.Equal(expected, actual)
+
[]
let ``Hints for discriminated union case fields are not shown when names are generated`` () =
let code =