diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index da89cec27a2..33afb3918b7 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -2,3 +2,5 @@ ### Added + +* Added warning FS3884 when a function or delegate value is used as an interpolated string argument. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289)) diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index 8ccb24cb207..d97ef294125 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -1,5 +1,6 @@ ### Added +* Warn (FS3884) when a function or delegate value is used as an interpolated string argument, since it will be formatted via `ToString` rather than being applied. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289)) * Added `MethodOverloadsCache` language feature (preview) that caches overload resolution results for repeated method calls, significantly improving compilation performance. ([PR #19072](https://github.com/dotnet/fsharp/pull/19072)) ### Fixed diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 4a406192316..47eaaf8ff25 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7502,6 +7502,15 @@ and TcFormatStringExpr cenv (overallTy: OverallTy) env m tpenv (fmtString: strin ) /// Check an interpolated string expression +and [] warnForFunctionValuesInFillExprs (g: TcGlobals) argTys synFillExprs = + match argTys, synFillExprs with + | argTy :: restTys, (synFillExpr: SynExpr) :: restExprs -> + if isFunTy g argTy || isDelegateTy g argTy then + warning (Error(FSComp.SR.tcFunctionValueUsedAsInterpolatedStringArg (), synFillExpr.Range)) + + warnForFunctionValuesInFillExprs g restTys restExprs + | _ -> () + and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: SynInterpolatedStringPart list) = let g = cenv.g @@ -7651,6 +7660,9 @@ and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: Syn // Type check the expressions filling the holes let fillExprs, tpenv = TcExprsNoFlexes cenv env m tpenv argTys synFillExprs + if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then + warnForFunctionValuesInFillExprs g argTys synFillExprs + // Take all interpolated string parts and typed fill expressions // and convert them to typed expressions that can be used as args to System.String.Concat // return an empty list if there are some format specifiers that make lowering to not applicable @@ -7720,6 +7732,9 @@ and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: Syn // Type check the expressions filling the holes let fillExprs, tpenv = TcExprsNoFlexes cenv env m tpenv argTys synFillExprs + if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then + warnForFunctionValuesInFillExprs g argTys synFillExprs + let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m) let dotnetFormatStringExpr = mkString g m dotnetFormatString diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index d24c8aba0eb..a4007147b9a 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1805,10 +1805,12 @@ featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type an 3878,tcAttributeIsNotValidForUnionCaseWithFields,"This attribute is not valid for use on union cases with fields." 3879,xmlDocNotFirstOnLine,"XML documentation comments should be the first non-whitespace text on a line." featureReturnFromFinal,"Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder." +featureWarnWhenFunctionValueUsedAsInterpolatedStringArg,"Warn when a function value is used as an interpolated string argument" featureMethodOverloadsCache,"Support for caching method overload resolution results for improved compilation performance." featureImplicitDIMCoverage,"Implicit dispatch slot coverage for default interface member implementations" featurePreprocessorElif,"#elif preprocessor directive" 3880,optsLangVersionOutOfSupport,"Language version '%s' is out of support. The last .NET SDK supporting it is available at https://dotnet.microsoft.com/en-us/download/dotnet/%s" 3881,optsUnrecognizedLanguageFeature,"Unrecognized language feature name: '%s'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'." 3882,lexHashElifMustBeFirst,"#elif directive must appear as the first non-whitespace character on a line" -3883,lexHashElifMustHaveIdent,"#elif directive should be immediately followed by an identifier" \ No newline at end of file +3883,lexHashElifMustHaveIdent,"#elif directive should be immediately followed by an identifier" +3884,tcFunctionValueUsedAsInterpolatedStringArg,"This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 0303881b08d..1bc31837052 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -104,6 +104,7 @@ type LanguageFeature = | ErrorOnInvalidDeclsInTypeDefinitions | AllowTypedLetUseAndBang | ReturnFromFinal + | WarnWhenFunctionValueUsedAsInterpolatedStringArg | MethodOverloadsCache | ImplicitDIMCoverage | PreprocessorElif @@ -248,6 +249,7 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) // F# 11.0 // Put stabilized features here for F# 11.0 previews via .NET SDK preview channels + LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg, languageVersion110 LanguageFeature.PreprocessorElif, languageVersion110 // Difference between languageVersion110 and preview - 11.0 gets turned on automatically by picking a preview .NET 11 SDK @@ -446,6 +448,8 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) | LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions -> FSComp.SR.featureErrorOnInvalidDeclsInTypeDefinitions () | LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens () | LanguageFeature.ReturnFromFinal -> FSComp.SR.featureReturnFromFinal () + | LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg -> + FSComp.SR.featureWarnWhenFunctionValueUsedAsInterpolatedStringArg () | LanguageFeature.MethodOverloadsCache -> FSComp.SR.featureMethodOverloadsCache () | LanguageFeature.ImplicitDIMCoverage -> FSComp.SR.featureImplicitDIMCoverage () | LanguageFeature.PreprocessorElif -> FSComp.SR.featurePreprocessorElif () diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index 20ea1175ec8..fd6182c9d3e 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -95,6 +95,7 @@ type LanguageFeature = | ErrorOnInvalidDeclsInTypeDefinitions | AllowTypedLetUseAndBang | ReturnFromFinal + | WarnWhenFunctionValueUsedAsInterpolatedStringArg | MethodOverloadsCache | ImplicitDIMCoverage | PreprocessorElif diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index e385cfda204..73bc8597430 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -101,7 +101,7 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta task { match! f fileName |> Async.StartAsTask with | Some source -> return SourceTextNew.ofISourceText source - | None -> return failwith $"Couldn't get source for file {f}" + | None -> return failwith $"Couldn't get source for file {fileName}" } ) diff --git a/src/Compiler/Utilities/DependencyGraph.fs b/src/Compiler/Utilities/DependencyGraph.fs index f36e8ac29d4..c3870e66b58 100644 --- a/src/Compiler/Utilities/DependencyGraph.fs +++ b/src/Compiler/Utilities/DependencyGraph.fs @@ -329,7 +329,7 @@ type GraphExtensions = static member Unpack(node: 'NodeValue, unpacker) = match unpacker node with | Some value -> value - | None -> failwith $"Expected {unpacker} but got: {node}" + | None -> failwith $"Expected unpacker to match but got: {node}" [] static member UnpackOne(dependencies: 'NodeValue seq, unpacker: 'NodeValue -> 'UnpackedDependency option) = @@ -337,14 +337,14 @@ type GraphExtensions = |> Seq.tryExactlyOne |> Option.bind unpacker |> Option.defaultWith (fun () -> - failwith $"Expected exactly one dependency matching {unpacker} but got: %A{dependencies |> Seq.toArray}") + failwith $"Expected exactly one dependency matching unpacker but got: %A{dependencies |> Seq.toArray}") [] static member UnpackMany(dependencies: 'NodeValue seq, unpacker) = let results = dependencies |> Seq.choose unpacker if dependencies |> Seq.length <> (results |> Seq.length) then - failwith $"Expected all dependencies to match {unpacker} but got: %A{dependencies |> Seq.toArray}" + failwith $"Expected all dependencies to match unpacker but got: %A{dependencies |> Seq.toArray}" results @@ -361,7 +361,7 @@ type GraphExtensions = | None, None -> extras.Add dependency |> ignore match oneResult with - | None -> failwith $"Expected exactly one dependency matching {oneUnpacker} but didn't find any" + | None -> failwith $"Expected exactly one dependency matching oneUnpacker but didn't find any" | Some head -> if extras.Count > 0 then failwith $"Found extra dependencies: %A{extras.ToArray()}" diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 154cdfbb025..6896c33a6a4 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -8952,6 +8952,16 @@ Rozšíření správce závislostí {0} nešlo načíst. Zpráva: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 2d7c8dcf008..3966a59a853 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -8952,6 +8952,16 @@ Die Abhängigkeits-Manager-Erweiterung "{0}" konnte nicht geladen werden. Meldung: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 19e1fe88759..08828606dd6 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -8952,6 +8952,16 @@ No se pudo cargar la extensión del administrador de dependencias {0}. Mensaje: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 98e2001eecf..5a28ec15953 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -8952,6 +8952,16 @@ Impossible de charger l'extension du gestionnaire de dépendances {0}. Message : {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 5dade539805..5dead052c6a 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -8952,6 +8952,16 @@ Non è stato possibile caricare l'estensione {0} di gestione delle dipendenze. Messaggio: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 500ab3771f0..f491fb0c4c5 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -8952,6 +8952,16 @@ 依存関係マネージャーの拡張機能 {0} を読み込むことができませんでした。メッセージ: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index a682e62da48..f8185fb2a22 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -8952,6 +8952,16 @@ 종속성 관리자 확장 {0}을(를) 로드할 수 없습니다. 메시지: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 05159019056..7e81e135eeb 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -8952,6 +8952,16 @@ Nie można załadować rozszerzenia menedżera zależności {0}. Komunikat: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 62d9febc99a..22a5db4504c 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -8952,6 +8952,16 @@ Não foi possível carregar a extensão do gerenciador de dependências {0}. Mensagem: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 9c9d89c904f..8f5220ba47e 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -8952,6 +8952,16 @@ Не удалось загрузить расширение диспетчера зависимостей {0}. Сообщение: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index d0576bf131a..17509827124 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -8952,6 +8952,16 @@ {0} bağımlılık yöneticisi uzantısı yüklenemedi. İleti: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index f1e43c884d0..bf26625f8ec 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -8952,6 +8952,16 @@ 无法加载依赖项管理器扩展 {0}。消息: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 48039c1413c..2a63e5ad753 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -8952,6 +8952,16 @@ 無法載入相依性管理員延伸模組 {0}。訊息: {1} + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + + + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments. + + \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs index 02157e7cf67..61eae80d184 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs @@ -183,6 +183,163 @@ printfn "%%s" (System.Globalization.CultureInfo "en-US" |> x.ToString) |> shouldSucceed |> withStdOutContains "abcde" + [] + let ``Warn when lambda is used as interpolated string argument`` () = + Fsx """ +let f = fun x -> x + 1 +let s = $"{f}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 3, Col 12, Line 3, Col 13, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``Warn when underscore dot shorthand is used as interpolated string argument`` () = + Fsx """ +type R = { Name: string } +let r = { Name = "hello" } +let s = $"{_.Name}" : string + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 4, Col 12, Line 4, Col 18, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``Warn when partially applied function is used as interpolated string argument`` () = + Fsx """ +let add x y = x + y +let s = $"{add 1}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 3, Col 12, Line 3, Col 17, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``Warn when named function is used as interpolated string argument`` () = + Fsx """ +let myFunc (x: int) = string x +let s = $"result: {myFunc}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 3, Col 20, Line 3, Col 26, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``No warn when non-function value is used as interpolated string argument`` () = + Fsx """ +let x = 42 +let s1 = $"{x}" +let s2 = $"{System.DateTime.Now}" + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``No warn when function is applied in interpolated string argument`` () = + Fsx """ +let f x = x + 1 +let s = $"{f 42}" + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``No warn for function value in interpolated string with older language version`` () = + Fsx """ +let f = fun x -> x + 1 +let s = $"{f}" + """ + |> withLangVersion10 + |> compile + |> shouldSucceed + + [] + let ``Warn when multiple function values are used in interpolated string`` () = + Fsx """ +let f x = x + 1 +let g x = x * 2 +let s = $"{f} and {g}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Warning 3884, Line 4, Col 12, Line 4, Col 13, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + (Warning 3884, Line 4, Col 20, Line 4, Col 21, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + ] + + [] + let ``Warn for function value in FormattableString interpolated string`` () = + Fsx """ +let f x = x + 1 +let s : System.FormattableString = $"{f}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 3, Col 39, Line 3, Col 40, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``Warn for function value with format specifier in interpolated string`` () = + Fsx """ +let f x = x + 1 +let s = $"{f:N2}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withDiagnostics [ + (Warning 3884, Line 3, Col 12, Line 3, Col 13, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + ] + + [] + let ``Warn can be suppressed with nowarn`` () = + Fsx """ +#nowarn "3884" +let f x = x + 1 +let s = $"{f}" + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Warn when System.Action delegate is used as interpolated string argument`` () = + Fsx """ +let a = System.Action(fun () -> ()) +let s = $"{a}" + """ + |> withLangVersionPreview + |> compile + |> withDiagnosticMessageMatches "This expression is a function value" + + [] + let ``Warn when System.Func delegate is used as interpolated string argument`` () = + Fsx """ +let f = System.Func(fun x -> string x) +let s = $"{f}" + """ + |> withLangVersionPreview + |> compile + |> shouldFail + |> withSingleDiagnostic (Warning 3884, Line 3, Col 12, Line 3, Col 13, "This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments.") + + [] + let ``No warn when delegate is invoked in interpolated string argument`` () = + Fsx """ +let f = System.Func(fun x -> string x) +let s = $"{f.Invoke(42)}" + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + // See https://github.com/dotnet/fsharp/issues/19367. [] let ``Hole without specifier parsed correctly when culture set to Thai`` () =