diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index c4e4a6a1403..9d59ae385ab 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -30,6 +30,7 @@ * Type parameter constraint `null` in generic code will now automatically imply `not struct` ([Issue #18320](https://github.com/dotnet/fsharp/issues/18320), [PR #18323](https://github.com/dotnet/fsharp/pull/18323)) * Add a switch to determine whether to generate a default implementation body for overridden method when completing. [PR #18341](https://github.com/dotnet/fsharp/pull/18341) +* Support `CallerArgumentExpression` ([Language Suggestion #966](https://github.com/fsharp/fslang-suggestions/issues/966), [PR #17519](https://github.com/dotnet/fsharp/pull/17519)) ### Changed * FSharpCheckFileResults.ProjectContext.ProjectOptions will not be available when using the experimental Transparent Compiler feature. ([PR #18205](https://github.com/dotnet/fsharp/pull/18205)) diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index 905e086a163..f9e6ec0ccd2 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -5,6 +5,7 @@ * Added type conversions cache, only enabled for compiler runs ([PR#17668](https://github.com/dotnet/fsharp/pull/17668)) * Support ValueOption + Struct attribute as optional parameter for methods ([Language suggestion #1136](https://github.com/fsharp/fslang-suggestions/issues/1136), [PR #18098](https://github.com/dotnet/fsharp/pull/18098)) * Warn when `unit` is passed to an `obj`-typed argument ([PR #18330](https://github.com/dotnet/fsharp/pull/18330)) +* Support `CallerArgumentExpression` ([Language Suggestion #966](https://github.com/fsharp/fslang-suggestions/issues/966), [PR #17519](https://github.com/dotnet/fsharp/pull/17519)) ### Fixed diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 41d731f007e..ae3150e59ba 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -1437,9 +1437,20 @@ let AdjustCallerArgExpr tcVal (g: TcGlobals) amap infoReader ad isOutArg calledA /// matter what order they are applied in as long as they are all composed together. let emptyPreBinder (e: Expr) = e +/// Try to pick the code text of an argument with the given parameter name from a list of assigned arguments. +let tryPickArgumentCodeText assignedArgs paramName = + assignedArgs + |> List.tryPick (fun { CalledArg=called; CallerArg=caller } -> + match called.NameOpt with + | Some x when x.idText = paramName -> + let code = FileContent.getCodeText caller.Range + if System.String.IsNullOrEmpty code then None + else Some code + | _ -> None) + /// Get the expression that must be inserted on the caller side for a CallerSide optional arg, /// i.e. one where there is no corresponding caller arg. -let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: CalledArg) currCalledArgTy currDfltVal eCallerMemberName mMethExpr = +let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: CalledArg) currCalledArgTy currDfltVal eCallerMemberName mMethExpr assignedArgs = match currDfltVal with | MissingValue -> // Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mkExprAddrOfExpr. @@ -1456,7 +1467,7 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C let ctorArgs = [Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, inst)] emptyPreBinder, Expr.Op (TOp.ILCall (false, false, true, true, NormalValUse, false, false, ctor, [inst], [], [currCalledArgTy]), [], ctorArgs, mMethExpr) | ByrefTy g inst -> - GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg inst (PassByRef(inst, currDfltVal)) eCallerMemberName mMethExpr + GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg inst (PassByRef(inst, currDfltVal)) eCallerMemberName mMethExpr assignedArgs | _ -> match calledArg.CallerInfo, eCallerMemberName with | CallerLineNumber, _ when typeEquiv g currCalledArgTy g.int_ty -> @@ -1466,6 +1477,14 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C emptyPreBinder, Expr.Const (Const.String fileName, mMethExpr, currCalledArgTy) | CallerMemberName, Some callerName when (typeEquiv g currCalledArgTy g.string_ty) -> emptyPreBinder, Expr.Const (Const.String callerName, mMethExpr, currCalledArgTy) + + | CallerArgumentExpression param, _ when g.langVersion.SupportsFeature LanguageFeature.SupportCallerArgumentExpression && typeEquiv g currCalledArgTy g.string_ty -> + let stringConst = + match tryPickArgumentCodeText assignedArgs param with + | Some code -> Const.String code + | None -> tcFieldInit mMethExpr fieldInit + emptyPreBinder, Expr.Const (stringConst, mMethExpr, currCalledArgTy) + | _ -> emptyPreBinder, Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, currCalledArgTy) @@ -1489,13 +1508,13 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C | PassByRef (ty, dfltVal2) -> let v, _ = mkCompGenLocal mMethExpr "defaultByrefArg" ty - let wrapper2, rhs = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg currCalledArgTy dfltVal2 eCallerMemberName mMethExpr + let wrapper2, rhs = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg currCalledArgTy dfltVal2 eCallerMemberName mMethExpr assignedArgs (wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr false (mkLocalValRef v) /// Get the expression that must be inserted on the caller side for a CalleeSide optional arg where /// no caller argument has been provided. Normally this is 'None', however CallerMemberName and friends /// can be used with 'CalleeSide' optional arguments -let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCallerMemberName (mMethExpr: range) = +let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCallerMemberName (mMethExpr: range) assignedArgs = let calledArgTy = calledArg.CalledArgumentType let calledNonOptTy = tryDestOptionalTy g calledArgTy @@ -1510,13 +1529,21 @@ let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCalle | CallerMemberName, Some(callerName) when typeEquiv g calledNonOptTy g.string_ty -> let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy) mkSome g calledNonOptTy memberNameExpr mMethExpr + + | CallerArgumentExpression param, _ when g.langVersion.SupportsFeature LanguageFeature.SupportCallerArgumentExpression && typeEquiv g calledNonOptTy g.string_ty -> + match tryPickArgumentCodeText assignedArgs param with + | Some code -> + let expr = Expr.Const(Const.String code, mMethExpr, calledNonOptTy) + mkSome g calledNonOptTy expr mMethExpr + | None -> mkNone g calledNonOptTy mMethExpr + | _ -> mkOptionalNone g calledArgTy calledNonOptTy mMethExpr /// Get the expression that must be inserted on the caller side for an optional arg where /// no caller argument has been provided. -let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCallerMemberName mItem (mMethExpr: range) = +let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCallerMemberName mItem (mMethExpr: range) assignedArgs = let calledArgTy = calledArg.CalledArgumentType let preBinder, expr = match calledArg.OptArgInfo with @@ -1524,10 +1551,10 @@ let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCal error(InternalError("Unexpected NotOptional", mItem)) | CallerSide dfltVal -> - GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName mMethExpr + GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName mMethExpr assignedArgs | CalleeSide -> - emptyPreBinder, GetDefaultExpressionForCalleeSideOptionalArg g calledArg eCallerMemberName mMethExpr + emptyPreBinder, GetDefaultExpressionForCalleeSideOptionalArg g calledArg eCallerMemberName mMethExpr assignedArgs // Combine the variable allocators (if any) let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr) @@ -1581,7 +1608,7 @@ let AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName (infoReader: mkOptionToNullable g m (destOptionTy g callerArgTy) callerArgExpr else // CSharpMethod(?x=b) when 'b' has optional type and 'x' has non-nullable type --> CSharpMethod(x=Option.defaultValue DEFAULT v) - let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m + let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m [assignedArg] let ty = destOptionTy g callerArgTy mkOptionDefaultValue g m ty defaultExpr callerArgExpr else @@ -1645,7 +1672,7 @@ let AdjustCallerArgsForOptionals tcVal tcFieldInit eCallerMemberName (infoReader // i.e. there is no corresponding caller arg. let optArgs, optArgPreBinder = (emptyPreBinder, calledMeth.UnnamedCalledOptArgs) ||> List.mapFold (fun preBinder calledArg -> - let preBinder2, arg = GetDefaultExpressionForOptionalArg tcFieldInit g calledArg eCallerMemberName mItem mMethExpr + let preBinder2, arg = GetDefaultExpressionForOptionalArg tcFieldInit g calledArg eCallerMemberName mItem mMethExpr (assignedNamedArgs @ unnamedArgs) arg, (preBinder >> preBinder2)) let adjustedNormalUnnamedArgs = List.map (AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName infoReader ad) unnamedArgs diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 4df0185a2e5..6c641a951a3 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -2391,8 +2391,10 @@ let CheckEntityDefn cenv env (tycon: Entity) = if numCurriedArgSets > 1 && others |> List.exists (fun minfo2 -> not (IsAbstractDefaultPair2 minfo minfo2)) then errorR(Error(FSComp.SR.chkDuplicateMethodCurried(nm, NicePrint.minimalStringOfType cenv.denv ty), m)) + let paramDatas = minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst) + if numCurriedArgSets > 1 && - (minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst) + (paramDatas |> List.existsSquared (fun (ParamData(isParamArrayArg, _isInArg, isOutArg, optArgInfo, callerInfo, _, reflArgInfo, ty)) -> isParamArrayArg || isOutArg || reflArgInfo.AutoQuote || optArgInfo.IsOptional || callerInfo <> NoCallerInfo || isByrefLikeTy g m ty)) then errorR(Error(FSComp.SR.chkCurriedMethodsCantHaveOutParams(), m)) @@ -2406,7 +2408,20 @@ let CheckEntityDefn cenv env (tycon: Entity) = if not ((isOptionTy g ty) && (typeEquiv g g.string_ty (destOptionTy g ty))) then errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "string", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m)) - minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst) + let paramNames = HashSet() + paramDatas + |> List.iterSquared (fun (ParamData(_, _, _, _, _, nameOpt, _, _)) -> + nameOpt |> Option.iter (fun name -> paramNames.Add name.idText |> ignore)) + + let checkArgOfCallerArgumentExpression m arg (nameOpt: Ident option) = + match nameOpt with + | Some ident when arg = ident.idText -> + warning(Error(FSComp.SR.tcCallerArgumentExpressionSelfReferential(), m)) + | _ when not (paramNames.Contains arg) -> + warning(Error(FSComp.SR.tcCallerArgumentExpressionHasInvalidParameterName(), m)) + | _ -> () + + paramDatas |> List.iterSquared (fun (ParamData(_, isInArg, _, optArgInfo, callerInfo, nameOpt, _, ty)) -> ignore isInArg @@ -2426,6 +2441,14 @@ let CheckEntityDefn cenv env (tycon: Entity) = errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "int", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m)) | CallerSide _, (CallerFilePath | CallerMemberName) -> errorIfNotStringTy m ty callerInfo | CalleeSide, (CallerFilePath | CallerMemberName) -> errorIfNotStringOptionTy m ty callerInfo + | CallerSide _, CallerArgumentExpression arg -> + checkLanguageFeatureAndRecover g.langVersion LanguageFeature.SupportCallerArgumentExpression m + errorIfNotStringTy m ty callerInfo + checkArgOfCallerArgumentExpression m arg nameOpt + | CalleeSide, CallerArgumentExpression arg -> + checkLanguageFeatureAndRecover g.langVersion LanguageFeature.SupportCallerArgumentExpression m + errorIfNotStringOptionTy m ty callerInfo + checkArgOfCallerArgumentExpression m arg nameOpt ) for pinfo in immediateProps do diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs index 8dcc5dc8ac1..056dacd40c9 100644 --- a/src/Compiler/Checking/infos.fs +++ b/src/Compiler/Checking/infos.fs @@ -240,6 +240,7 @@ type CallerInfo = | CallerLineNumber | CallerMemberName | CallerFilePath + | CallerArgumentExpression of paramName: string override x.ToString() = sprintf "%+A" x @@ -317,20 +318,25 @@ let CrackParamAttribsInfo g (ty: TType, argInfo: ArgReprInfo) = let isCallerLineNumberArg = HasFSharpAttribute g g.attrib_CallerLineNumberAttribute argInfo.Attribs let isCallerFilePathArg = HasFSharpAttribute g g.attrib_CallerFilePathAttribute argInfo.Attribs let isCallerMemberNameArg = HasFSharpAttribute g g.attrib_CallerMemberNameAttribute argInfo.Attribs + let callerArgumentExpressionArg = + TryFindFSharpAttributeOpt g g.attrib_CallerArgumentExpressionAttribute argInfo.Attribs + |> Option.orElseWith (fun () -> TryFindFSharpAttributeByName "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" argInfo.Attribs) let callerInfo = - match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg with - | false, false, false -> NoCallerInfo - | true, false, false -> CallerLineNumber - | false, true, false -> CallerFilePath - | false, false, true -> CallerMemberName - | false, true, true -> + match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg, callerArgumentExpressionArg with + | false, false, false, None -> NoCallerInfo + | true, false, false, None -> CallerLineNumber + | false, true, false, None -> CallerFilePath + | false, false, true, None -> CallerMemberName + | false, false, false, Some(Attrib(_, _, (AttribStringArg x :: _), _, _, _, _)) -> + CallerArgumentExpression(x) + | false, true, true, _ -> match TryFindFSharpAttribute g g.attrib_CallerMemberNameAttribute argInfo.Attribs with | Some(Attrib(_, _, _, _, _, _, callerMemberNameAttributeRange)) -> warning(Error(FSComp.SR.CallerMemberNameIsOverridden(argInfo.Name.Value.idText), callerMemberNameAttributeRange)) CallerFilePath | _ -> failwith "Impossible" - | _, _, _ -> + | _, _, _, _ -> // if multiple caller info attributes are specified, pick the "wrong" one here // so that we get an error later match tryDestOptionTy g ty with @@ -1280,14 +1286,20 @@ type MethInfo = let isCallerLineNumberArg = TryFindILAttribute g.attrib_CallerLineNumberAttribute attrs let isCallerFilePathArg = TryFindILAttribute g.attrib_CallerFilePathAttribute attrs let isCallerMemberNameArg = TryFindILAttribute g.attrib_CallerMemberNameAttribute attrs + let isCallerArgumentExpressionArg = + g.attrib_CallerArgumentExpressionAttribute + |> Option.bind (fun (AttribInfo(tref, _)) -> TryDecodeILAttribute tref attrs) + |> Option.orElseWith (fun () -> TryDecodeILAttributeByName "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" attrs) let callerInfo = - match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg with - | false, false, false -> NoCallerInfo - | true, false, false -> CallerLineNumber - | false, true, false -> CallerFilePath - | false, false, true -> CallerMemberName - | _, _, _ -> + match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg, isCallerArgumentExpressionArg with + | false, false, false, None -> NoCallerInfo + | true, false, false, None -> CallerLineNumber + | false, true, false, None -> CallerFilePath + | false, false, true, None -> CallerMemberName + | false, false, false, Some ([ILAttribElem.String (Some name) ], _) -> CallerArgumentExpression(name) + | false, false, false, _ -> NoCallerInfo + | _, _, _, _ -> // if multiple caller info attributes are specified, pick the "wrong" one here // so that we get an error later if p.Type.TypeRef.FullName = "System.Int32" then CallerFilePath diff --git a/src/Compiler/Checking/infos.fsi b/src/Compiler/Checking/infos.fsi index e091834e271..c8a2cdc5e8e 100644 --- a/src/Compiler/Checking/infos.fsi +++ b/src/Compiler/Checking/infos.fsi @@ -101,6 +101,7 @@ type CallerInfo = | CallerLineNumber | CallerMemberName | CallerFilePath + | CallerArgumentExpression of paramName: string [<RequireQualifiedAccess>] type ReflectedArgInfo = diff --git a/src/Compiler/Driver/CompilerConfig.fs b/src/Compiler/Driver/CompilerConfig.fs index cccbcb15810..35cd81ce100 100644 --- a/src/Compiler/Driver/CompilerConfig.fs +++ b/src/Compiler/Driver/CompilerConfig.fs @@ -1539,3 +1539,14 @@ type TcConfigProvider = TcConfigProvider(fun _ctok -> TcConfig.Create(tcConfigB, validate = false)) let GetFSharpCoreLibraryName () = getFSharpCoreLibraryName + +/// Read and store the source file content for the `CallerArgumentExpression` feature. +/// This should only be called in `fsc` or `fsi` processing. +let readAndStoreFileContents (tcConfig: TcConfig) (sourceFiles: #seq<string>) = + if + tcConfig.langVersion.SupportsFeature LanguageFeature.SupportCallerArgumentExpression + && (tcConfig.compilationMode.IsOneOff || tcConfig.isInteractive) + then + for fileName in sourceFiles do + if FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) then + FileContent.update fileName (FileContent.FileCacheType.NotYetRead tcConfig.inputCodePage) diff --git a/src/Compiler/Driver/CompilerConfig.fsi b/src/Compiler/Driver/CompilerConfig.fsi index 0e6c25727f8..13d234840dc 100644 --- a/src/Compiler/Driver/CompilerConfig.fsi +++ b/src/Compiler/Driver/CompilerConfig.fsi @@ -961,3 +961,7 @@ val FSharpMLCompatFileSuffixes: string list /// Indicates whether experimental features should be enabled automatically val FSharpExperimentalFeaturesEnabledAutomatically: bool + +/// Read and store the source file content for the `CallerArgumentExpression` feature. +/// This should only be called in `fsc` or `fsi` processing. +val readAndStoreFileContents: tcConfig: TcConfig -> sourceFiles: #seq<string> -> unit diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 3518d9d4884..bb338e2dccf 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -671,6 +671,9 @@ let main1 // Build the initial type checking environment ReportTime tcConfig "Typecheck" + // Read the source file content for the `CallerArgumentExpression` feature + readAndStoreFileContents tcConfig sourceFiles + use unwindParsePhase = UseBuildPhase BuildPhase.TypeCheck let tcEnv0, openDecls0 = diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 9bfa12ce963..63a53e7dad3 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1798,3 +1798,6 @@ featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted" featureSupportValueOptionsAsOptionalParameters,"Support ValueOption as valid type for optional member parameters" featureSupportWarnWhenUnitPassedToObjArg,"Warn when unit is passed to a member accepting `obj` argument, e.g. `Method(o:obj)` will warn if called via `Method()`." +featureSupportCallerArgumentExpression,"Support `CallerArgumentExpression`" +3875,tcCallerArgumentExpressionSelfReferential,"The CallerArgumentExpression on this parameter will have no effect because it's self-referential." +3875,tcCallerArgumentExpressionHasInvalidParameterName,"The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 7a9a14b8602..131bd35a16b 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -99,6 +99,7 @@ type LanguageFeature = | DeprecatePlacesWhereSeqCanBeOmitted | SupportValueOptionsAsOptionalParameters | WarnWhenUnitPassedToObjArg + | SupportCallerArgumentExpression /// LanguageVersion management type LanguageVersion(versionText) = @@ -229,6 +230,7 @@ type LanguageVersion(versionText) = LanguageFeature.DeprecatePlacesWhereSeqCanBeOmitted, previewVersion LanguageFeature.SupportValueOptionsAsOptionalParameters, previewVersion LanguageFeature.WarnWhenUnitPassedToObjArg, previewVersion + LanguageFeature.SupportCallerArgumentExpression, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") @@ -391,6 +393,7 @@ type LanguageVersion(versionText) = | LanguageFeature.DeprecatePlacesWhereSeqCanBeOmitted -> FSComp.SR.featureDeprecatePlacesWhereSeqCanBeOmitted () | LanguageFeature.SupportValueOptionsAsOptionalParameters -> FSComp.SR.featureSupportValueOptionsAsOptionalParameters () | LanguageFeature.WarnWhenUnitPassedToObjArg -> FSComp.SR.featureSupportWarnWhenUnitPassedToObjArg () + | LanguageFeature.SupportCallerArgumentExpression -> FSComp.SR.featureSupportCallerArgumentExpression () /// 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 410a8b193c9..027ba9f9a43 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -90,6 +90,7 @@ type LanguageFeature = | DeprecatePlacesWhereSeqCanBeOmitted | SupportValueOptionsAsOptionalParameters | WarnWhenUnitPassedToObjArg + | SupportCallerArgumentExpression /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 78d5cb94dcd..e266647aee7 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -807,6 +807,9 @@ type internal FsiValuePrinter(fsi: FsiEvaluationSessionHostConfig, outWriter: Te type internal FsiStdinSyphon(errorWriter: TextWriter) = let syphonText = StringBuilder() + /// Get the current syphon text + member _.SyphonText = syphonText.ToString() + /// Clears the syphon text member _.Reset() = syphonText.Clear() |> ignore @@ -2899,6 +2902,9 @@ type internal FsiDynamicCompiler let istate = fsiDynamicCompiler.ProcessDelayedReferences(ctok, istate) + // Read the source file content for the `CallerArgumentExpression` feature + readAndStoreFileContents tcConfig sourceFiles + fsiDynamicCompiler.EvalParsedSourceFiles(ctok, diagnosticsLogger, istate, inputs, m) member _.GetBoundValues istate = @@ -3511,7 +3517,11 @@ type FsiStdinLexerProvider |> Option.iter (fun t -> match t with | Null -> () - | NonNull t -> fsiStdinSyphon.Add(t + "\n")) + | NonNull t -> + fsiStdinSyphon.Add(t + "\n") + // Update the stdin file content for the `CallerArgumentExpression` feature + FileContent.clear () + FileContent.update stdinMockFileName (FileContent.FileCacheType.FromString fsiStdinSyphon.SyphonText)) match inputOption with | Some Null @@ -4208,6 +4218,9 @@ type FsiInteractionProcessor ProcessStepStatus status None (fun _ istate -> run istate) + // Read the source file content for the `CallerArgumentExpression` feature + readAndStoreFileContents tcConfig [ sourceFile ] + run istate) /// Load the source files, one by one. Called on the main thread. @@ -4285,6 +4298,11 @@ type FsiInteractionProcessor currState |> InteractiveCatch diagnosticsLogger (fun istate -> let expr = ParseInteraction tokenizer + + // Update the file content for the `CallerArgumentExpression` feature + FileContent.clear () + FileContent.update scriptFileName (FileContent.FileCacheType.FromString sourceText) + ExecuteParsedInteractionOnMainThread(ctok, diagnosticsLogger, expr, istate, cancellationToken)) |> commitResult @@ -4319,6 +4337,10 @@ type FsiInteractionProcessor SynExprSequentialTrivia.Zero ) + // Update the file content for the `CallerArgumentExpression` feature + FileContent.clear () + FileContent.update scriptFileName (FileContent.FileCacheType.FromString sourceText) + ExecuteParsedExpressionOnMainThread(ctok, diagnosticsLogger, exprWithSeq, istate)) |> commitResult diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 93dd8905553..6947f8df265 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -1092,7 +1092,6 @@ type TcGlobals( // Adding an unnecessary "let" instead of inlining into a multi-line pipelined compute-once "member val" that is too complex for @dsyme let v_attribs_Unsupported = [ tryFindSysAttrib "System.Runtime.CompilerServices.ModuleInitializerAttribute" - tryFindSysAttrib "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" tryFindSysAttrib "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute" tryFindSysAttrib "System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute" tryFindSysAttrib "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute" @@ -1503,6 +1502,7 @@ type TcGlobals( member val attrib_ExtensionAttribute = findSysAttrib "System.Runtime.CompilerServices.ExtensionAttribute" member val attrib_CallerLineNumberAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerLineNumberAttribute" member val attrib_CallerFilePathAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerFilePathAttribute" + member val attrib_CallerArgumentExpressionAttribute = tryFindSysAttrib "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" member val attrib_CallerMemberNameAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerMemberNameAttribute" member val attrib_SkipLocalsInitAttribute = findSysAttrib "System.Runtime.CompilerServices.SkipLocalsInitAttribute" member val attrib_DecimalConstantAttribute = findSysAttrib "System.Runtime.CompilerServices.DecimalConstantAttribute" diff --git a/src/Compiler/TypedTree/TcGlobals.fsi b/src/Compiler/TypedTree/TcGlobals.fsi index b8c3610ef91..59a8f8ed2e2 100644 --- a/src/Compiler/TypedTree/TcGlobals.fsi +++ b/src/Compiler/TypedTree/TcGlobals.fsi @@ -336,6 +336,8 @@ type internal TcGlobals = member attrib_CallerLineNumberAttribute: BuiltinAttribInfo + member attrib_CallerArgumentExpressionAttribute: BuiltinAttribInfo option + member attrib_CallerMemberNameAttribute: BuiltinAttribInfo member attrib_ClassAttribute: BuiltinAttribInfo diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index e5e330759dc..bf88932ea38 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3519,12 +3519,25 @@ let HasILAttribute tref (attrs: ILAttributes) = let TryDecodeILAttribute tref (attrs: ILAttributes) = attrs.AsArray() |> Array.tryPick (fun x -> if isILAttrib tref x then Some(decodeILAttribData x) else None) +let TryDecodeILAttributeByName nm (attrs: ILAttributes) = + attrs.AsArray() |> Array.tryPick (fun x -> if isILAttribByName ([], nm) x then Some(decodeILAttribData x) else None) + // F# view of attributes (these get converted to AbsIL attributes in ilxgen) let IsMatchingFSharpAttribute g (AttribInfo(_, tcref)) (Attrib(tcref2, _, _, _, _, _, _)) = tyconRefEq g tcref tcref2 let HasFSharpAttribute g tref attrs = List.exists (IsMatchingFSharpAttribute g tref) attrs let TryFindFSharpAttribute g tref attrs = List.tryFind (IsMatchingFSharpAttribute g tref) attrs let TryFindFSharpAttributeOpt g tref attrs = match tref with None -> None | Some tref -> List.tryFind (IsMatchingFSharpAttribute g tref) attrs +let TryFindFSharpAttributeByName nm attrs = + let path, typeName = splitILTypeName nm + attrs + |> List.tryFind (fun (Attrib(tcref2, _, _, _, _, _, _)) -> + match tcref2.TryDeref with + | ValueNone -> false + | ValueSome x -> + x.CompilationPath.MangledPath = path && + x.CompiledName = typeName) + let HasFSharpAttributeOpt g trefOpt attrs = match trefOpt with Some tref -> List.exists (IsMatchingFSharpAttribute g tref) attrs | _ -> false let IsMatchingFSharpAttributeOpt g attrOpt (Attrib(tcref2, _, _, _, _, _, _)) = match attrOpt with Some (AttribInfo(_, tcref)) -> tyconRefEq g tcref tcref2 | _ -> false diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index c67ccb30800..23c985f655b 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2315,6 +2315,8 @@ val mkLdelem: TcGlobals -> range -> TType -> Expr -> Expr -> Expr val TryDecodeILAttribute: ILTypeRef -> ILAttributes -> (ILAttribElem list * ILAttributeNamedArg list) option +val TryDecodeILAttributeByName: nm: string -> ILAttributes -> (ILAttribElem list * ILAttributeNamedArg list) option + val IsILAttrib: BuiltinAttribInfo -> ILAttribute -> bool val TryFindILAttribute: BuiltinAttribInfo -> ILAttributes -> bool @@ -2329,6 +2331,8 @@ val HasFSharpAttribute: TcGlobals -> BuiltinAttribInfo -> Attribs -> bool val HasFSharpAttributeOpt: TcGlobals -> BuiltinAttribInfo option -> Attribs -> bool +val TryFindFSharpAttributeByName: nm: string -> Attribs -> Attrib option + val TryFindFSharpAttribute: TcGlobals -> BuiltinAttribInfo -> Attribs -> Attrib option val TryFindFSharpAttributeOpt: TcGlobals -> BuiltinAttribInfo option -> Attribs -> Attrib option diff --git a/src/Compiler/Utilities/range.fs b/src/Compiler/Utilities/range.fs index 5e13752df0b..10734d0a77a 100755 --- a/src/Compiler/Utilities/range.fs +++ b/src/Compiler/Utilities/range.fs @@ -581,3 +581,83 @@ module Range = | None -> mkRange file (mkPos 1 0) (mkPos 1 80) with _ -> mkRange file (mkPos 1 0) (mkPos 1 80) + +module internal FileContent = + [<RequireQualifiedAccess>] + type FileCacheType = + | AlreadyRead of lines: string array + | NotYetRead of codePage: int option + + static member FromString(s: string) = AlreadyRead(s.Split '\n') + + let private fileContentDict = ConcurrentDictionary<string, FileCacheType>() + + let update (fileName: string) (fileContent: FileCacheType) = + fileContentDict.AddOrUpdate(fileName, (fun _ -> fileContent), (fun _ _ -> fileContent)) + |> ignore + + let clear () = fileContentDict.Clear() + + /// Get the substring of the given range. + let private substring (input: string array) (range: range) = + let startLine, startCol = range.StartLine, range.StartColumn + let endLine, endCol = range.EndLine, range.EndColumn + + if + (startCol < 0 || endCol < 0) + || startLine < 1 + || startLine > endLine + || startLine > input.Length + || (startLine = endLine + && (startCol > endCol || startCol >= input.[startLine - 1].Length)) + then + System.String.Empty + elif startLine = endLine then + let line = input.[startLine - 1] + let endCol = min (endCol - 1) (line.Length - 1) + let result = line.Substring(startCol, endCol - startCol + 1) + + if endCol = line.Length - 1 && line[endCol] = '\r' then + result + "\n" + else + result + else + let appendNewLineMark sb = + (sb: System.Text.StringBuilder).Append '\n' |> ignore + + let startCol = min startCol (input.[startLine - 1].Length - 1) + let result = System.Text.StringBuilder() + result.Append(input.[startLine - 1].Substring(startCol)) |> ignore + appendNewLineMark result + + let upperBound = min (endLine - 2) (input.Length - 1) + + for i in startLine..upperBound do + result.Append(input.[i]) |> ignore + appendNewLineMark result + + if endLine < input.Length then + let line = input.[endLine - 1] + let endCol = min endCol line.Length + result.Append(line.Substring(0, endCol)) |> ignore + + if endCol = line.Length - 1 && line[endCol] = '\r' then + appendNewLineMark result + + result.ToString() + + let getCodeText (m: range) = + let fileName = m.FileName + + match fileContentDict.TryGetValue fileName with + | true, FileCacheType.AlreadyRead lines -> substring lines m + | true, FileCacheType.NotYetRead codePage -> + try + use fileStream = FileSystem.OpenFileForReadShim fileName + use reader = fileStream.GetReader(codePage) + let lines = reader.ReadToEnd().Split('\n') + update fileName (FileCacheType.AlreadyRead lines) + substring lines m + with _ -> + String.Empty + | _ -> String.Empty diff --git a/src/Compiler/Utilities/range.fsi b/src/Compiler/Utilities/range.fsi index 40a52efa879..363b8bee927 100755 --- a/src/Compiler/Utilities/range.fsi +++ b/src/Compiler/Utilities/range.fsi @@ -269,4 +269,21 @@ module Line = /// Convert a line number from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio) val toZ: int -> Line0 - +/// Store code file content. Used to implement `CallerArgumentExpression` +module internal FileContent = + [<RequireQualifiedAccess>] + type FileCacheType = + | AlreadyRead of lines: string array + | NotYetRead of codePage: int option + + static member FromString: s: string -> FileCacheType + + /// Update the file content. Should be called before the code file/expression is typechecked. + val update: fileName: string -> fileContent: FileCacheType -> unit + + /// Clean up all the file contents. + /// May be called in `fsi` processing, to avoid holding the source files which were compiled. + val clear: unit -> unit + + /// Get the code text of the specific `range` + val getCodeText: range -> string diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 7d64601c303..4bfdfcd169a 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -632,6 +632,11 @@ <target state="translated">reprezentace struktury aktivních vzorů</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Atributy nejde použít pro rozšíření typů.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Tento výraz záznamu kopírování a aktualizace mění všechna pole typu záznamu '{0}'. Zvažte použití syntaxe konstrukce záznamu.</target> diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 67f4270377e..f17ffc16cf5 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -632,6 +632,11 @@ <target state="translated">Strukturdarstellung für aktive Muster</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Attribute können nicht auf Typerweiterungen angewendet werden.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Dieser Ausdruck zum Kopieren und Aktualisieren von Datensätzen ändert alle Felder des Datensatztyps "{0}". Erwägen Sie stattdessen die Verwendung der Datensatzerstellungssyntax.</target> diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 9478e3d2c55..481e70bcb61 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -632,6 +632,11 @@ <target state="translated">representación de struct para modelos activos</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Los atributos no se pueden aplicar a las extensiones de tipo.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Esta expresión de copia y actualización de registros cambia todos los campos de tipo de registro "{0}". Es preferible utilizar la sintaxis de construcción de registros.</target> diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 57fade5d250..dc09b017ef6 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -632,6 +632,11 @@ <target state="translated">représentation de structure pour les modèles actifs</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Impossible d'appliquer des attributs aux extensions de type.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Cette expression d'enregistrement de copie et de mise à jour modifie tous les champs du type d'enregistrement '{0}'. Envisagez d'utiliser la syntaxe de construction d'enregistrement à la place.</target> diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 14d670e8455..9871c03dd56 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -632,6 +632,11 @@ <target state="translated">rappresentazione struct per criteri attivi</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Gli attributi non possono essere applicati a estensioni di tipo.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Questa espressione di record di copia e aggiornamento modifica tutti i campi del tipo di record '{0}'. Provare a usare la sintassi di costruzione dei record.</target> diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 78acb0fd944..fceebe13b2c 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -632,6 +632,11 @@ <target state="translated">アクティブなパターンの構造体表現</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">属性を型拡張に適用することはできません。</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">この copy-and-update レコード式は、レコードの種類が '{0}' であるすべてのフィールドを変更します。代わりにレコード構築構文を使用することを検討してください。</target> diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 604ac431e4a..0d063580dbc 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -632,6 +632,11 @@ <target state="translated">활성 패턴에 대한 구조체 표현</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">형식 확장에 특성을 적용할 수 없습니다.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">이 레코드 복사 및 업데이트 식은 '{0}' 레코드 형식의 모든 필드를 변경합니다. 레코드 생성 구문을 대신 사용하는 것이 좋습니다.</target> diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index fdee3d82f7d..d74c66459f0 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -632,6 +632,11 @@ <target state="translated">reprezentacja struktury aktywnych wzorców</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Atrybutów nie można stosować do rozszerzeń typu.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">To wyrażenie rekordu kopiowania i aktualizacji zmienia wszystkie pola typu rekordu „{0}”. Zamiast tego rozważ użycie składni konstrukcji rekordu.</target> diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 2f86c57d960..88ee21f19d3 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -632,6 +632,11 @@ <target state="translated">representação estrutural para padrões ativos</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Os atributos não podem ser aplicados às extensões de tipo.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Essa expressão de registro copiar e atualizar altera todos os campos do tipo de registro '{0}'. Considere usar a sintaxe de construção de registro em vez disso.</target> diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index fefd5255a0b..0826b26c6c1 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -632,6 +632,11 @@ <target state="translated">представление структуры для активных шаблонов</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Атрибуты не могут быть применены к расширениям типа.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Это выражение записи копирования и обновления изменяет все поля типа записи "{0}". Вместо этого можно использовать синтаксис конструкции записи.</target> diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 5325f0ab09f..6ce660c6191 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -632,6 +632,11 @@ <target state="translated">etkin desenler için yapı gösterimi</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">Öznitelikler tür uzantılarına uygulanamaz.</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">Bu kopyalama ve güncelleştirme kayıt ifadesi, '{0}' kayıt türündeki tüm alanları değiştirir. Bunun yerine kayıt oluşturma söz dizimini kullanmayı deneyin.</target> diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 2e8b957d810..9c6d632af32 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -632,6 +632,11 @@ <target state="translated">活动模式的结构表示形式</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">属性不可应用于类型扩展。</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">此复制和更新记录表达式更改记录类型“{0}”的所有字段。请考虑改用记录构造语法。</target> diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index f0924b3d30f..68d7523eee5 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -632,6 +632,11 @@ <target state="translated">現用模式的結構表示法</target> <note /> </trans-unit> + <trans-unit id="featureSupportCallerArgumentExpression"> + <source>Support `CallerArgumentExpression`</source> + <target state="new">Support `CallerArgumentExpression`</target> + <note /> + </trans-unit> <trans-unit id="featureSupportValueOptionsAsOptionalParameters"> <source>Support ValueOption as valid type for optional member parameters</source> <target state="new">Support ValueOption as valid type for optional member parameters</target> @@ -1362,6 +1367,16 @@ <target state="translated">屬性無法套用到類型延伸模組。</target> <note /> </trans-unit> + <trans-unit id="tcCallerArgumentExpressionHasInvalidParameterName"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.</target> + <note /> + </trans-unit> + <trans-unit id="tcCallerArgumentExpressionSelfReferential"> + <source>The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</source> + <target state="new">The CallerArgumentExpression on this parameter will have no effect because it's self-referential.</target> + <note /> + </trans-unit> <trans-unit id="tcCopyAndUpdateRecordChangesAllFields"> <source>This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.</source> <target state="translated">此複製和更新記錄運算式將變更記錄類型為 '{0}' 的所有欄位。請考慮改用記錄建構語法。</target> diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/CallerArgumentExpression.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/CallerArgumentExpression.fs new file mode 100644 index 00000000000..81dec3701fa --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/CallerArgumentExpression.fs @@ -0,0 +1,384 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Conformance.BasicGrammarElements + +open Xunit +open FSharp.Test.Compiler +open FSharp.Test + +module CustomAttributes_CallerArgumentExpression = + + [<FactForNETCOREAPP>] + let ``Can consume CallerArgumentExpression in BCL methods`` () = + let path = __SOURCE_DIRECTORY__ ++ "test script.fsx" + FsFromPath path + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``Can define methods using CallerArgumentExpression with C#-style optional arguments`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices +type A() = + static member aa ( + a, + [<CallerMemberName; Optional; DefaultParameterValue "no value">]b: string, + [<CallerLineNumber; Optional; DefaultParameterValue 0>]c: int, + [<CallerArgumentExpressionAttribute("a"); Optional; DefaultParameterValue "no value">]e: string) = + a,b,c,e + +let stringABC = "abc" +assertEqual (A.aa(stringABC)) ("abc", ".cctor", 13, "stringABC") +assertEqual (A.aa(a = stringABC)) ("abc", ".cctor", 14, "stringABC") + """ + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``Can define methods using CallerArgumentExpression with F#-style optional arguments`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices +type A() = + static member aa ( + a, + [<CallerMemberName>] ?b: string, + [<CallerLineNumber>] ?c: int, + [<CallerArgumentExpressionAttribute("a")>] ?e: string) = + let b = defaultArg b "no value" + let c = defaultArg c 0 + let e = defaultArg e "no value" + a,b,c,e + +let stringABC = "abc" +assertEqual (A.aa(stringABC)) ("abc", ".cctor", 16, "stringABC") +assertEqual (A.aa(a = stringABC)) ("abc", ".cctor", 17, "stringABC") + """ + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP(Skip = "Currently cannot get the original text range with #line")>] + let ``Can define in F# - with #line`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +let path = System.IO.Path.Combine(__SOURCE_DIRECTORY__, "test.fs") + +# 1 "test.fs" +type A() = + static member aa ( + a, + [<CallerMemberName; Optional; DefaultParameterValue "no value">]b: string, + [<CallerLineNumber; Optional; DefaultParameterValue 0>]c: int, + [<CallerFilePath; Optional; DefaultParameterValue "no value">]d: string, + [<CallerArgumentExpressionAttribute("a"); Optional; DefaultParameterValue "no value">]e: string) = + a,b,c,d,e + + static member B (``ab c``, [<CallerArgumentExpression "ab c">]?n) = + defaultArg n "no value" + +let stringABC = "abc" +assertEqual (A.aa(stringABC)) ("abc", ".cctor", 11, path, "stringABC") +# 1 "test.fs" +assertEqual (A.aa(stringABC : string)) ("abc", ".cctor", 1, path, "stringABC : string") +# 1 "test.fs" +assertEqual (A.aa(a = (stringABC : string))) ("abc", ".cctor", 1, path, "(stringABC : string)") + + +A.B("abc" +#line 1 +: string) +|> assertEqual "\"abc\" +#line 1 +: string" + + +A.B((+) 1 +#line 1 + 123) +|> assertEqual "(+) 1 +#line 1 + 123" + + +A.B(#line 1 + (+) 1 + 123) +|> assertEqual "(+) 1 + 123" + """ + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``Can define methods using CallerArgumentExpression receiving special parameter names`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices + +type A() = + static member B (``ab c``, [<CallerArgumentExpression "ab c">]?n) = + defaultArg n "no value" + +assertEqual (A.B("abc")) "\"abc\"" + """ + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``test Warns when cannot find the referenced parameter or self-referential`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices + +type A() = + static member A (``ab c``, [<CallerArgumentExpression "abc">]?n) = + defaultArg n "no value" + static member B (``ab c``, [<CallerArgumentExpression "n">] ?n) = + defaultArg n "no value" + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 3875,Line 5, Col 65, Line 5, Col 66, "The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name.") + (Warning 3875,Line 7, Col 65 , Line 7, Col 66, "The CallerArgumentExpression on this parameter will have no effect because it's self-referential.") + ] + + [<FactForNETCOREAPP>] + let ``test Errors`` () = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +type A() = + static member A (``ab c``, [<CallerArgumentExpression "ab c">] n) = + defaultArg n "no value" + static member B (``ab c``, [<CallerArgumentExpression "ab c">]?n) = + defaultArg n 123 + static member C (``ab c``, [<CallerArgumentExpression "ab c"; Optional; DefaultParameterValue 0>] n: int) = + n + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1247,Line 6, Col 66, Line 6, Col 67, "'CallerArgumentExpression \"ab c\"' can only be applied to optional arguments") + (Error 1246,Line 8, Col 66, Line 8, Col 67, "'CallerArgumentExpression \"ab c\"' must be applied to an argument of type 'string', but has been applied to an argument of type 'int'") + (Error 1246,Line 10, Col 101, Line 10, Col 102, "'CallerArgumentExpression \"ab c\"' must be applied to an argument of type 'string', but has been applied to an argument of type 'int'") + ] + + [<Fact>] + let ``User can define the CallerArgumentExpression`` () = + FSharp """namespace System.Runtime.CompilerServices + +open System + +[<AttributeUsage(AttributeTargets.Parameter, AllowMultiple=false, Inherited=false)>] +type CallerArgumentExpressionAttribute(parameterName: string) = + inherit Attribute() + + member val ParameterName = parameterName + +namespace global +module A = + let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b + open System.Runtime.CompilerServices + + type A() = + static member B (``ab c``, [<CallerArgumentExpression "ab c">]?n) = + defaultArg n "no value" + + A.B "abc" |> assertEqual "\"abc\"" + A.B ("abc": string) |> assertEqual "\"abc\": string" + A.B ("abc": (* comments *) string) |> assertEqual "\"abc\": (* comments *) string" +""" + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``Can use with Computation Expression`` = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +type Builder() = + member self.Bind( + x, f, + [<CallerArgumentExpression "x">] ?exp : string, + [<CallerArgumentExpression "f">] ?exp2 : string) = + (f x, $"f={exp2.Value}, x={exp.Value}") + + member self.Return(x, [<CallerArgumentExpression "x">] ?exp : string) = + (x, $"x={exp.Value}") + +let b = Builder() +b { do! () } |> assertEqual (((), "x=do!"), "f=do!, x=()") +b { let! a = 123 in return a } |> assertEqual ((123, "x=a"), "f=return a, x=123") + +b { + let! a = 123 + let! b = 456 + return a + b +} |> assertEqual + (((579, "x=a + b"), "f=return a + b, x=456"), + "f=let! b = 456 + return a + b, x=123") +""" + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + + [<FactForNETCOREAPP>] + let ``Can use with Delegate and Quotation`` = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +type A = + delegate of + a: int * + [<CallerArgumentExpression "a">] ?expr: string * + [<CallerArgumentExpression "a"; Optional; DefaultParameterValue "">] expr2: string + -> string * string option +let a = A (fun a expr expr2 -> expr2, expr) +a.Invoke(123 - 7) |> assertEqual ("123 - 7", Some "123 - 7") + +open Microsoft.FSharp.Quotations.Patterns +match <@ a.Invoke(123 - 7) @> with +| Call(_, _, [_; Value (:? (string * string option) as value, _)]) -> assertEqual ("123 - 7", Some "123 - 7") value +| _ -> failwith "fail" +""" + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForNETCOREAPP>] + let ``Can use with Interface and Object Expression`` = + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +type Interface1 = + abstract member M: + a: int * + [<CallerArgumentExpression "a">] ?expr: string * + [<CallerArgumentExpression "a"; Optional; DefaultParameterValue "">] expr2: string + -> string * string option + +{new Interface1 with + member this.M(a, expr, expr2) = expr2, expr}.M(123 - 7) |> assertEqual ("123 - 7", Some "123 - 7") +""" + |> withLangVersionPreview + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + (* ------------ C# Interop tests ------------- *) + [<FactForNETCOREAPP>] + let ``C# can consume methods using CallerArgumentExpression receiving special parameter names`` () = + let fs = + FSharp """module Lib +let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +type A() = + static member B (``ab c``, [<CallerArgumentExpression "ab c"; Optional; DefaultParameterValue "no value">]n: string) = + n + """ + |> withLangVersionPreview + + CSharp """Lib.assertEqual(Lib.A.B("abc"), "\"abc\"");""" + |> withName "CSLib" + |> withReferences [fs] + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + [<FactForDESKTOP>] + let ``Can recognize CallerArgumentExpression defined in C#`` () = + let cs = + CSharp """using System.Runtime.CompilerServices; +public class AInCs +{ + public static string B(int param, [CallerArgumentExpression("param")] string expr = null) => expr; +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string param) + { + Param = param; + } + + public string Param { get; } + } +} +""" + + FSharp """let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +open System.Runtime.CompilerServices + +type A() = + static member B (``ab c``, [<CallerArgumentExpression "ab c">]?n) = + defaultArg n "no value" + +A.B "abc" |> assertEqual "\"abc\"" +AInCs.B (123 - 7) |> assertEqual "123 - 7" + """ + |> withLangVersionPreview + |> withReferences [cs] + |> asExe + |> compileAndRun + |> shouldSucceed + |> ignore + + (* ------------ FSI tests ------------- *) + + [<FactForNETCOREAPP>] + let ``Check in fsi`` () = + let path = __SOURCE_DIRECTORY__ ++ "test script.fsx" + FsxFromPath path + |> withLangVersionPreview + |> runFsi + |> shouldSucceed + |> ignore + + + [<FactForNETCOREAPP>] + let ``Check fsi #load`` () = + let path = __SOURCE_DIRECTORY__ ++ "test script.fsx" + Fsx $"""#load @"{path}" """ + |> withLangVersionPreview + |> runFsi + |> shouldSucceed + |> ignore diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/test script.fsx b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/test script.fsx new file mode 100644 index 00000000000..c2152fa6b3b --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/CallerArgumentExpression/test script.fsx @@ -0,0 +1,17 @@ +let assertEqual a b = if a <> b then failwithf "not equal: %A and %A" a b +try System.ArgumentException.ThrowIfNullOrWhiteSpace(Seq.init 50 (fun _ -> " ") + (* comment *) + |> String.concat " ") +with :? System.ArgumentException as ex -> + assertEqual true (ex.Message.Contains("(Parameter 'Seq.init 50 (fun _ -> \" \") + (* comment *) + |> String.concat \" \"")) + + +try System.ArgumentException.ThrowIfNullOrWhiteSpace(argument = (Seq.init 11 (fun _ -> " ") + (* comment *) + |> String.concat " ")) +with :? System.ArgumentException as ex -> + assertEqual true (ex.Message.Contains("(Parameter '(Seq.init 11 (fun _ -> \" \") + (* comment *) + |> String.concat \" \")")) \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnsupportedAttributes.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnsupportedAttributes.fs index 3eb78de55b4..295e5115c49 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnsupportedAttributes.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnsupportedAttributes.fs @@ -15,7 +15,7 @@ open System.Runtime.CompilerServices let f (w, [<CallerArgumentExpression "w">] x : string) = () let [<ModuleInitializer>] g () = () type C() = - member _.F (w, [<System.Runtime.CompilerServices.CallerArgumentExpression "w">] x : string) = () + [<System.Runtime.CompilerServices.ModuleInitializer>] member _.G() = () """ @@ -23,13 +23,6 @@ type C() = |> typecheck |> shouldFail |> withResults [ - { Error = Warning 202 - Range = { StartLine = 3 - StartColumn = 13 - EndLine = 3 - EndColumn = 41 } - Message = - "This attribute is currently unsupported by the F# compiler. Applying it will not achieve its intended effect." } { Error = Warning 202 Range = { StartLine = 4 StartColumn = 7 @@ -37,13 +30,6 @@ type C() = EndColumn = 24 } Message = "This attribute is currently unsupported by the F# compiler. Applying it will not achieve its intended effect." } - { Error = Warning 202 - Range = { StartLine = 6 - StartColumn = 22 - EndLine = 6 - EndColumn = 82 } - Message = - "This attribute is currently unsupported by the F# compiler. Applying it will not achieve its intended effect." } { Error = Warning 202 Range = { StartLine = 7 StartColumn = 7 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 60b867c7815..a9af0d57eee 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -40,6 +40,7 @@ <Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\PermittedLocations\PermittedLocations.fs" /> <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeInheritance\AttributeInheritance.fs" /> <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeUsage\AttributeUsage.fs" /> + <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\CallerArgumentExpression\CallerArgumentExpression.fs" /> <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\Basic\Basic.fs" /> <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ImportedAttributes\ImportedAttributes.fs" /> <Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ArgumentsOfAllTypes\ArgumentsOfAllTypes.fs" /> @@ -352,6 +353,7 @@ <ItemGroup> <Content Include="resources\**" CopyToOutputDirectory="Never" CopyToPublishDirectory="PreserveNewest" /> <EmbeddedResource Remove="Properties\**" /> + <None Remove="Conformance\BasicGrammarElements\CustomAttributes\CallerArgumentExpression.fs" /> <None Remove="EmittedIL\GenericComparison\CrossAssembly.fs" /> <None Remove="ErrorMessages\ActivePatternArgCountMismatchTest.fs" /> <Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" /> diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl old mode 100755 new mode 100644 diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index 4466d718308..da3d394d76a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x00000209][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index d5617f6de03..188df6b5a7a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-805::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x00000209][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. @@ -84,6 +84,8 @@ [IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment+probePathForDotnetHost@316::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000028][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+Pipe #6 input at line 68@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2<string,string>[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.Unit>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<bool,Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.Unit>>>)][offset 0x0000034D][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000CD][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Text.FileContent::getCodeText([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000098][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Text.FileContent+FileCacheType::FromString(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [CallVirtOnValueType]: : FSharp.Compiler.Text.RangeModule+comparer@558::System.Collections.Generic.IEqualityComparer<FSharp.Compiler.Text.Range>.GetHashCode([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000002] Callvirt on a value type method. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000037][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000043][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 9e78cdedccd..de04825a10c 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001FC][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@921-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 87b09e193a5..dfdd1660d6c 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -28,8 +28,11 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3502-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x00000107][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-849::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x00000208][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x00000024][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+EvalInteraction@4299::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState)][offset 0x0000002E][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+EvalExpression@4326::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState)][offset 0x00000059][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1<FSharp.Compiler.EditorServices.AssemblySymbol>&)][offset 0x000000BB][found Char] Unexpected type on the stack. @@ -111,6 +114,8 @@ [IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment::probePathForDotnetHost@315([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000002A][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+SimulatedMSBuildResolver@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2<string,string>[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.Unit>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<bool,Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.FSharpFunc`2<string,Microsoft.FSharp.Core.Unit>>>)][offset 0x000002F5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000B6][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Text.FileContent::getCodeText([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000089][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Text.FileContent+FileCacheType::FromString(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [CallVirtOnValueType]: : FSharp.Compiler.Text.RangeModule+comparer@558::System.Collections.Generic.IEqualityComparer<FSharp.Compiler.Text.Range>.GetHashCode([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000002] Callvirt on a value type method. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000035][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000041][found Char] Unexpected type on the stack.