From 02c3d53a155b1abea1a6e063aa1f0ba8194f595d Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 13 Feb 2026 21:33:18 +0100 Subject: [PATCH 1/7] Fixup: Extract DRY warning helper, restore missing test assertion - Extract duplicated warning block into warnForFunctionValuesInFillExprs local function - Restore accidentally deleted withStdOutContains assertion in FormattableString test --- .../Checking/Expressions/CheckExpressions.fs | 11 ++ src/Compiler/FSComp.txt | 4 +- src/Compiler/Facilities/LanguageFeatures.fs | 4 + src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/xlf/FSComp.txt.cs.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.de.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.es.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.fr.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.it.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.ja.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.ko.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.pl.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.ru.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.tr.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 10 ++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 10 ++ .../Language/InterpolatedStringsTests.fs | 128 +++++++++++++++++- 18 files changed, 276 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 3d93b2c526d..d0c6dacb6b8 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7592,6 +7592,13 @@ and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: Syn if argTys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) + let warnForFunctionValuesInFillExprs () = + if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then + (argTys, synFillExprs) + ||> List.iter2 (fun argTy synFillExpr -> + if isFunTy g argTy then + warning (Error(FSComp.SR.tcFunctionValueUsedAsInterpolatedStringArg (), synFillExpr.Range))) + match stringKind with // The case for $"..." used as type string and $"...%d{x}..." used as type PrintfFormat - create a PrintfFormat that captures @@ -7615,6 +7622,8 @@ 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 + warnForFunctionValuesInFillExprs () + // 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 @@ -7684,6 +7693,8 @@ 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 + warnForFunctionValuesInFillExprs () + 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 b39bf210809..e082feb5a1e 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1803,5 +1803,7 @@ 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" 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'." \ No newline at end of file +3881,optsUnrecognizedLanguageFeature,"Unrecognized language feature name: '%s'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'." +3882,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." \ No newline at end of file diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 9fb00722263..c4a5daa83dd 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -104,6 +104,7 @@ type LanguageFeature = | ErrorOnInvalidDeclsInTypeDefinitions | AllowTypedLetUseAndBang | ReturnFromFinal + | WarnWhenFunctionValueUsedAsInterpolatedStringArg /// LanguageVersion management type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) = @@ -245,6 +246,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 // Difference between languageVersion110 and preview - 11.0 gets turned on automatically by picking a preview .NET 11 SDK // previewVersion is only when "preview" is specified explicitly in project files and users also need a preview SDK @@ -440,6 +442,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 () /// 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 09cb4273571..2f57e9b0b2b 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -95,6 +95,7 @@ type LanguageFeature = | ErrorOnInvalidDeclsInTypeDefinitions | AllowTypedLetUseAndBang | ReturnFromFinal + | WarnWhenFunctionValueUsedAsInterpolatedStringArg /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 85dbf4dd95c..47f00246d3a 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Sdílení podkladových polí v rozlišeném sjednocení [<Struct>] za předpokladu, že mají stejný název a typ @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Nerozpoznaná hodnota {0} pro parametr --langversion; seznam možností zobrazíte zadáním --langversion:? diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 370e248e1d6..49393a98488 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Teilen sie zugrunde liegende Felder in einen [<Struct>]-diskriminierten Union, solange sie denselben Namen und Typ aufweisen. @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Unbekannter Wert "{0}" für "--langversion". Verwenden Sie "--langversion:?", um die vollständige Liste anzuzeigen. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index c760506b835..5a5852ad173 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Compartir campos subyacentes en una unión discriminada [<Struct>] siempre y cuando tengan el mismo nombre y tipo @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Valor no reconocido "{0}" para --langversion, use --langversion:? para una lista completa diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 94935d998ff..acf333f1ab2 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Partager les champs sous-jacents dans une union discriminée [<Struct>] tant qu’ils ont le même nom et le même type @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Valeur non reconnue '{0}' pour --langversion use --langversion:? pour la liste complète diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index e83cb026ed3..7835e6d2472 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Condividi i campi sottostanti in un'unione discriminata di [<Struct>] purché abbiano lo stesso nome e tipo @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Valore '{0}' non riconosciuto per --langversion. Per l'elenco completo usare --langversion:? diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 9888850c875..daea914e7ad 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type 名前と型が同じである限り、[<Struct>] 判別可能な共用体で基になるフィールドを共有する @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list --langversion の値 '{0}' が認識されません。完全なリストについては、--langversion:? を使用してください diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 433af22b44b..41f81d8ff68 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type 이름과 형식이 같으면 [<Struct>] 구분된 공용 구조체에서 기본 필드 공유 @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list 전체 목록에 대한 --langversion use --langversion:?의 인식할 수 없는 값 '{0}'입니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 8d167f8c5c4..bd570d212db 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Udostępnij pola źródłowe w unii rozłącznej [<Struct>], o ile mają taką samą nazwę i ten sam typ @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Nierozpoznana wartość „{0}” dla parametru –langversion; podaj parametr –langversion:?, aby uzyskać pełną listę diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 872af8ca069..7ac4656907f 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Compartilhar campos subjacentes em uma união discriminada [<Struct>], desde que tenham o mesmo nome e tipo @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Valor não reconhecido '{0}' para --langversion use --langversion:? para a lista completa diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 940bcf5df31..47ae941c039 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Совместное использование базовых полей в дискриминируемом объединении [<Struct>], если они имеют одинаковое имя и тип. @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list Не удалось распознать значение "{0}" для параметра --langversion. Для получения полного списка допустимых значений выполните команду use --langversion:? diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 28576ca243d..9cc384e53c5 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type Aynı ada ve türe sahip oldukları sürece temel alınan alanları [<Struct>] ayırt edici birleşim biçiminde paylaşın @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list --langversion için '{0}' değeri tanınmıyor. Tam liste için --langversion:? kullanın diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 6c459b3f33c..d57d65524e4 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type 只要它们具有相同的名称和类型,即可在 [<Struct>] 中共享基础字段 @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list --langversion 的值“{0}”无法识别,使用 --langversion:? 获取完整列表。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 296ef8fa7de..d4fb703248d 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -607,6 +607,11 @@ Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder. + + Warn when a function value is used as an interpolated string argument + Warn when a function value is used as an interpolated string argument + + Share underlying fields in a [<Struct>] discriminated union as long as they have same name and type 只要 [<Struct>] 具有相同名稱和類型,就以強制聯集共用基礎欄位 @@ -1082,6 +1087,11 @@ Unrecognized language feature name: '{0}'. Use a valid feature name such as 'NameOf' or 'StringInterpolation'. + + 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. + + Unrecognized value '{0}' for --langversion use --langversion:? for complete list 對 --langversion 為無法識別的值 '{0}',對完整清單使用 --langversion:? diff --git a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs index e7c18f0d679..1fa6162fa4f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs @@ -181,4 +181,130 @@ printfn "%%s" (System.Globalization.CultureInfo "en-US" |> x.ToString) """ |> compileExeAndRun |> shouldSucceed - |> withStdOutContains "abcde" \ No newline at end of file + |> 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 3882, 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 3882, 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 3882, 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 3882, 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 3882, 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 3882, 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 3882, 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 3882, 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 "3882" +let f x = x + 1 +let s = $"{f}" + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed \ No newline at end of file From 4298ffdf23f7b26f843fe458a56266795b38d859 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 16 Feb 2026 16:00:13 +0100 Subject: [PATCH 2/7] fix repo codebase --- .../Checking/Expressions/CheckExpressions.fs | 2 +- src/Compiler/Service/FSharpProjectSnapshot.fs | 2 +- src/Compiler/Utilities/DependencyGraph.fs | 8 ++--- .../Language/InterpolatedStringsTests.fs | 32 +++++++++++++++++++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index d0c6dacb6b8..e34a7b546c0 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7596,7 +7596,7 @@ and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: Syn if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then (argTys, synFillExprs) ||> List.iter2 (fun argTy synFillExpr -> - if isFunTy g argTy then + if isFunTy g argTy || isDelegateTy g argTy then warning (Error(FSComp.SR.tcFunctionValueUsedAsInterpolatedStringArg (), synFillExpr.Range))) match stringKind with 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/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs index 1fa6162fa4f..0f7a2523aeb 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs @@ -304,6 +304,38 @@ let s = $"{f:N2}" #nowarn "3882" 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 + |> shouldFail + |> withSingleDiagnostic (Warning 3882, 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 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 3882, 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 From de56786597db5baf9073a6f823fb4c02c0296fce Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 16 Feb 2026 16:23:19 +0100 Subject: [PATCH 3/7] eliminate closure allocs --- .../Checking/Expressions/CheckExpressions.fs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index e34a7b546c0..6c15df8f024 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -7466,6 +7466,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 @@ -7592,13 +7601,6 @@ and TcInterpolatedStringExpr cenv (overallTy: OverallTy) env m tpenv (parts: Syn if argTys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) - let warnForFunctionValuesInFillExprs () = - if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then - (argTys, synFillExprs) - ||> List.iter2 (fun argTy synFillExpr -> - if isFunTy g argTy || isDelegateTy g argTy then - warning (Error(FSComp.SR.tcFunctionValueUsedAsInterpolatedStringArg (), synFillExpr.Range))) - match stringKind with // The case for $"..." used as type string and $"...%d{x}..." used as type PrintfFormat - create a PrintfFormat that captures @@ -7622,7 +7624,8 @@ 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 - warnForFunctionValuesInFillExprs () + 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 @@ -7693,7 +7696,8 @@ 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 - warnForFunctionValuesInFillExprs () + if g.langVersion.SupportsFeature LanguageFeature.WarnWhenFunctionValueUsedAsInterpolatedStringArg then + warnForFunctionValuesInFillExprs g argTys synFillExprs let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m) From 84afd5016be88f967f0ab793a6ce1068c83f5896 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 16 Feb 2026 16:28:00 +0100 Subject: [PATCH 4/7] Add release notes for FS3882 warning Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/10.0.300.md | 2 ++ docs/release-notes/.Language/preview.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index 471abbfb982..f80d851363c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -16,6 +16,8 @@ ### Added +* Added warning FS3882 when a function or delegate value is used as an interpolated string argument. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289)) + ### Changed * Centralized product TFM (Target Framework Moniker) into MSBuild props file `eng/TargetFrameworks.props`. Changing the target framework now only requires editing one file, and it integrates with MSBuild's `--getProperty` for scripts. diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index 62dd231d664..4c455ab35d7 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -1,5 +1,7 @@ ### Added +* Warn (FS3882) 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)) + ### Fixed ### Changed \ No newline at end of file From 0cb26523d86609a423c1369093ad51e2114edbba Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 3 Mar 2026 14:57:36 +0100 Subject: [PATCH 5/7] Fix: renumber warning from 3882 to 3884 to avoid collision with #elif directive The PR's warning code 3882 collided with main's lexHashElifMustBeFirst (3882) added by the #elif preprocessor directive feature. Renumbered to 3884 (next available). Updated all tests, release notes, and xlf files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/10.0.300.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index 83ac754aecf..f7d1007c2f4 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -34,7 +34,7 @@ * FCS: capture additional types during analysis ([PR #19305](https://github.com/dotnet/fsharp/pull/19305)) -* Added warning FS3882 when a function or delegate value is used as an interpolated string argument. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289)) +* 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)) ### Changed From 8ccc6785a252c1664a3461d3c743821cc99f7d09 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 4 Mar 2026 11:06:39 +0100 Subject: [PATCH 6/7] Fix System.Action test to be resilient to extra diagnostics on Desktop Use withDiagnosticMessageMatches instead of withSingleDiagnostic for the System.Action delegate test, since net472 may produce additional diagnostics alongside warning 3884. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Language/InterpolatedStringsTests.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs index 5e5d662884b..61eae80d184 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/InterpolatedStringsTests.fs @@ -317,8 +317,7 @@ let s = $"{a}" """ |> 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.") + |> withDiagnosticMessageMatches "This expression is a function value" [] let ``Warn when System.Func delegate is used as interpolated string argument`` () = From 0e5f0bf70a38525d4f7538834cdbfb3381316545 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 6 Mar 2026 19:26:43 +0100 Subject: [PATCH 7/7] notes --- docs/release-notes/.FSharp.Compiler.Service/10.0.300.md | 2 -- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index 960acaafa76..057bf1d18a9 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -35,8 +35,6 @@ * FCS: capture additional types during analysis ([PR #19305](https://github.com/dotnet/fsharp/pull/19305)) -* 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)) - ### Changed * Centralized product TFM (Target Framework Moniker) into MSBuild props file `eng/TargetFrameworks.props`. Changing the target framework now only requires editing one file, and it integrates with MSBuild's `--getProperty` for scripts. 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))