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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing インデックス作成とスライス用の 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 인덱싱 및 슬라이싱을 위한 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 用于索引和切片的 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 + Improved implied argument names + + expr[idx] notation for indexing and slicing 用於編製索引和分割的 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 =