From b38ee87ec663ae9dfa67b8df1e925e5530fb58b6 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 23 Feb 2026 09:37:06 +0100 Subject: [PATCH 1/2] Fix ~150 code scanning warnings in src/Fable.Transforms/ Address five categories of analyzer warnings across 23 files: - GRA-STRING-001/002/004: Add StringComparison.Ordinal to string operations - IONIDE-007: Replace cons-nil patterns (x :: []) with list literals ([x]) - IONIDE-005: Replace empty string comparisons with String.IsNullOrEmpty - GRA-TYPE-ANNOTATE-001: Add type annotations to ambiguous `string` calls - GRA-INTERPOLATED-001: Add format specifiers (:s, :d) to interpolated strings Co-Authored-By: Claude Opus 4.6 --- src/Fable.Transforms/BabelPrinter.fs | 2 +- src/Fable.Transforms/Beam/Fable2Beam.fs | 2 +- src/Fable.Transforms/Dart/Fable2Dart.fs | 12 +- src/Fable.Transforms/Dart/Replacements.fs | 13 +- src/Fable.Transforms/FSharp2Fable.Util.fs | 6 +- src/Fable.Transforms/FSharp2Fable.fs | 4 +- src/Fable.Transforms/Fable2Babel.fs | 21 +-- src/Fable.Transforms/FableTransforms.fs | 2 +- src/Fable.Transforms/Global/Naming.fs | 4 +- src/Fable.Transforms/Global/Prelude.fs | 12 +- src/Fable.Transforms/OverloadSuffix.fs | 4 +- src/Fable.Transforms/Php/Fable2Php.fs | 2 +- src/Fable.Transforms/Python/Prelude.fs | 4 +- src/Fable.Transforms/Python/Replacements.fs | 6 +- src/Fable.Transforms/Replacements.Util.fs | 28 ++-- src/Fable.Transforms/Replacements.fs | 8 +- .../Rust/AST/Rust.AST.Adapters.fs | 4 +- .../Rust/AST/Rust.AST.Helpers.fs | 17 ++- .../Rust/AST/Rust.AST.State.fs | 2 +- src/Fable.Transforms/Rust/Fable2Rust.fs | 24 ++-- src/Fable.Transforms/Rust/Replacements.fs | 120 +++++++++--------- src/Fable.Transforms/State.fs | 6 +- src/Fable.Transforms/Transforms.Util.fs | 8 +- 23 files changed, 162 insertions(+), 149 deletions(-) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index 62344d6801..0825a02d89 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -447,7 +447,7 @@ module PrinterExtensions = |> List.iter ( function | _, NullOrUndefinedOrVoid -> () - | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) + | key, StringConstant value -> printProp (fun () -> printer.Print($"{key:s}=\"{value:s}\"")) | key, value -> printProp (fun () -> printer.Print(key + "={") diff --git a/src/Fable.Transforms/Beam/Fable2Beam.fs b/src/Fable.Transforms/Beam/Fable2Beam.fs index af96ec91ff..5258d1db13 100644 --- a/src/Fable.Transforms/Beam/Fable2Beam.fs +++ b/src/Fable.Transforms/Beam/Fable2Beam.fs @@ -1162,7 +1162,7 @@ and transformValue (com: IBeamCompiler) (ctx: Context) (value: ValueKind) : Beam else adjusted - Beam.ErlExpr.Literal(Beam.ErlLiteral.BigInt(string value)) + Beam.ErlExpr.Literal(Beam.ErlLiteral.BigInt(string value)) | NewRecord(values, ref, _genArgs) -> match com.TryGetEntity(ref) with diff --git a/src/Fable.Transforms/Dart/Fable2Dart.fs b/src/Fable.Transforms/Dart/Fable2Dart.fs index bd0169f731..2b4bdd6a08 100644 --- a/src/Fable.Transforms/Dart/Fable2Dart.fs +++ b/src/Fable.Transforms/Dart/Fable2Dart.fs @@ -178,7 +178,7 @@ module Util = match transformAndCaptureExpr com ctx entRef with | [], IdentExpression ident -> Some ident | _ -> - addError com [] None $"Unexpected, entity ref for {ent.FullName} is not an identifier" + addError com [] None $"Unexpected, entity ref for {ent.FullName:s} is not an identifier" None ) @@ -1953,17 +1953,17 @@ module Util = match transformCallArgs com ctx (CallInfo info) with | [], args -> args | _, args -> - $"Rewrite base arguments for {classDecl.Entity.FullName} so they can be compiled as Dart expressions" + $"Rewrite base arguments for {classDecl.Entity.FullName:s} so they can be compiled as Dart expressions" |> addWarning com [] e.Range args | Some(Fable.Value _ as e) -> - $"Ignoring base call for {classDecl.Entity.FullName}" + $"Ignoring base call for {classDecl.Entity.FullName:s}" |> addWarning com [] e.Range [] | Some e -> - $"Unexpected base call for {classDecl.Entity.FullName}" + $"Unexpected base call for {classDecl.Entity.FullName:s}" |> addError com [] e.Range [] @@ -2309,7 +2309,7 @@ module Util = let name = match p.Name with | Some name -> name - | None -> $"arg{i}$" + | None -> $"arg{i:d}$" let t = transformType com ctx p.Type FunctionArg(makeImmutableIdent t name) // TODO, isOptional=p.IsOptional, isNamed=p.IsNamed) @@ -2384,7 +2384,7 @@ module Util = = match implementsIterable, baseType with | Some iterable, Some _ -> - $"Types implementing IEnumerable cannot inherit from another class: {classEnt.FullName}" + $"Types implementing IEnumerable cannot inherit from another class: {classEnt.FullName:s}" |> addError com [] None Some iterable diff --git a/src/Fable.Transforms/Dart/Replacements.fs b/src/Fable.Transforms/Dart/Replacements.fs index 37d183d845..2b02c4e8bd 100644 --- a/src/Fable.Transforms/Dart/Replacements.fs +++ b/src/Fable.Transforms/Dart/Replacements.fs @@ -635,7 +635,7 @@ let tryEntityIdent (com: Compiler) entFullName = | BuiltinDefinition(FSharpReference _) -> makeImportLib com MetaType "FSharpRef" "Types" |> Some | BuiltinDefinition(FSharpResult _) -> makeImportLib com MetaType "FSharpResult$2" "Result" |> Some | BuiltinDefinition(FSharpChoice genArgs) -> - let membName = $"FSharpChoice${List.length genArgs}" + let membName = $"FSharpChoice${List.length genArgs:d}" makeImportLib com MetaType membName "Choice" |> Some // | BuiltinDefinition BclGuid -> jsTypeof "string" expr | BuiltinDefinition(BclHashSet _) @@ -665,7 +665,8 @@ let tryEntityIdent (com: Compiler) entFullName = | Types.averager | Types.icomparerGeneric | Types.iequalityComparerGeneric -> - let entFullName = entFullName[entFullName.LastIndexOf(".") + 1 ..] + let entFullName = + entFullName[entFullName.LastIndexOf(".", StringComparison.Ordinal) + 1 ..] let entFullName = match entFullName.IndexOf("`", StringComparison.Ordinal) with @@ -1386,7 +1387,7 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt | "GetEnumerator", Some c, _ -> stringToCharSeq c |> getEnumerator com r t |> Some | ("Contains" | "StartsWith" | "EndsWith" as meth), Some c, arg :: _ -> if List.isMultiple args then - addWarning com ctx.InlinePath r $"String.{meth}: second argument is ignored" + addWarning com ctx.InlinePath r $"String.{meth:s}: second argument is ignored" Helper.InstanceCall(c, Naming.lowerFirst meth, t, [ arg ], ?loc = r) |> Some | ReplaceName [ "ToUpper", "toUpperCase" @@ -3105,7 +3106,7 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opti | meth, Some thisArg -> let meth = if meth = "Next" then - $"Next{List.length args}" + $"Next{List.length args:d}" else meth @@ -3220,7 +3221,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth} doesn't support more than 2 arguments" + $"Regex.{meth:s} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4136,6 +4137,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index ab3c45518a..03d1434385 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -651,7 +651,7 @@ module Helpers = let join sep s o = (f s) - + (if o = "" then + + (if String.IsNullOrEmpty(o) then "" else sep + o) @@ -1123,7 +1123,7 @@ module Patterns = memb.IsPropertyGetterMethod && not memb.IsDispatchSlot && not memb.IsOverrideOrExplicitInterfaceImplementation - && memb.LogicalName.StartsWith("get_Is") + && memb.LogicalName.StartsWith("get_Is", StringComparison.Ordinal) then let unionCaseName = memb.LogicalName |> Naming.replacePrefix "get_Is" "" ent.UnionCases |> Seq.tryFind (fun uc -> uc.Name = unionCaseName) @@ -2530,7 +2530,7 @@ module Util = let failReplace (com: IFableCompiler) ctx r (info: Fable.ReplaceCallInfo) (thisArg: Fable.Expr option) = let msg = if info.DeclaringEntityFullName.StartsWith("Fable.Core.", StringComparison.Ordinal) then - $"{info.DeclaringEntityFullName}.{info.CompiledName} is not supported, try updating fable tool" + $"{info.DeclaringEntityFullName:s}.{info.CompiledName:s} is not supported, try updating fable tool" else com.WarnOnlyOnce( "Fable only supports a subset of standard .NET API, please check https://fable.io/docs/dotnet/compatibility.html. For external libraries, check whether they are Fable-compatible in the package docs." diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 18f55c831c..5d1cbefd2a 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -1648,7 +1648,7 @@ let private applyJsPyDecorators let parameters = memb.CurriedParameterGroups |> Seq.collect id - |> Seq.mapi (fun i p -> defaultArg p.Name $"arg{i}", makeType Map.empty p.Type) + |> Seq.mapi (fun i p -> defaultArg p.Name $"arg{i:d}", makeType Map.empty p.Type) |> Seq.toList Replacements.Api.makeMethodInfo com None name parameters returnType @@ -2614,7 +2614,7 @@ type FableCompiler(com: Compiler) = |> List.filter (fun (i, v) -> if ctx.CapturedBindings.Contains(i.Name) && canHaveSideEffects com v then if isIdentUsed i.Name resolved then - $"Inlined argument {i.Name} is being captured but is also used somewhere else. " + $"Inlined argument {i.Name:s} is being captured but is also used somewhere else. " + "There's a risk of double evaluation." |> addWarning com [] i.Range diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 2d6941143b..4135122928 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -743,10 +743,10 @@ module Annotation = | Replacements.Util.FSharpMap(key, value) -> makeFableLibImportTypeAnnotation com ctx [ key; value ] "Map" "FSharpMap" | Replacements.Util.FSharpResult(ok, err) -> - $"FSharpResult$2{Util.UnionHelpers.UNION_SUFFIX}" + $"FSharpResult$2{Util.UnionHelpers.UNION_SUFFIX:s}" |> makeFableLibImportTypeAnnotation com ctx [ ok; err ] "Result" | Replacements.Util.FSharpChoice genArgs -> - $"FSharpChoice${List.length genArgs}{Util.UnionHelpers.UNION_SUFFIX}" + $"FSharpChoice${List.length genArgs:d}{Util.UnionHelpers.UNION_SUFFIX:s}" |> makeFableLibImportTypeAnnotation com ctx genArgs "Choice" | Replacements.Util.FSharpReference genArg -> if isInRefOrAnyType com typ then @@ -761,7 +761,7 @@ module Annotation = | _ -> argTypes |> List.mapi (fun i argType -> FunctionTypeParam.functionTypeParam ( - Identifier.identifier ($"arg{i}"), + Identifier.identifier ($"arg{i:d}"), makeTypeAnnotation com ctx argType ) ) @@ -1022,7 +1022,7 @@ module Util = if selector.StartsWith("*", StringComparison.Ordinal) then selector else - $"default as {selector}" + $"default as {selector:s}" com.GetImportExpr(ctx, selector, path, r, noMangle = true) |> ignore @@ -1402,7 +1402,7 @@ module Util = |> List.map (fun (a, p) -> match p.Name with | Some name when p.IsNamed && a.Name <> name -> - $"Argument {name} is marked as named but conflicts with another name in scope" + $"Argument {name:s} is marked as named but conflicts with another name in scope" |> addWarning com [] a.Range | _ -> () @@ -1612,7 +1612,7 @@ module Util = Expression.callExpression (helperRef, values, typeArguments = typeParams) | None -> callConstructor (Some case) | None -> - $"Unmatched union case tag: {tag} for {ent.FullName}" |> addWarning com [] r + $"Unmatched union case tag: {tag:d} for {ent.FullName:s}" |> addWarning com [] r callConstructor None else @@ -2600,7 +2600,8 @@ but thanks to the optimisation done below we get match List.tryItem tag ent.UnionCases with | Some case -> Some case.Name | None -> - $"Unmatched union case tag: {tag} for {ent.FullName}" |> addWarning com [] range + $"Unmatched union case tag: {tag:d} for {ent.FullName:s}" + |> addWarning com [] range None | _ -> None @@ -4047,7 +4048,7 @@ but thanks to the optimisation done below we get "Unable to find a valid constructor for generating interface via ParamObject, please make sure the constructor has at least one parameter." [] - | members :: [] -> + | [ members ] -> Declaration.interfaceDeclaration ( Identifier.identifier decl.Name, members, @@ -4082,7 +4083,7 @@ but thanks to the optimisation done below we get let genericSuffix = if ent.GenericParameters.Length > 0 then - $"`{ent.GenericParameters.Length}" + $"`{ent.GenericParameters.Length:d}" else "" @@ -4565,7 +4566,7 @@ but thanks to the optimisation done below we get let noConflict = ctx.UsedNames.RootScope.Add(alias) if not noConflict then - com.WarnOnlyOnce($"Import {alias} conflicts with existing identifier in root scope") + com.WarnOnlyOnce($"Import {alias:s} conflicts with existing identifier in root scope") alias else diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 0504a57575..2f75ff0c96 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -567,7 +567,7 @@ module private Transforms = match expr with | IdentExpr _ -> None, expr | arg -> - let ident = makeTypedIdent argType $"anonRec{com.IncrementCounter()}" + let ident = makeTypedIdent argType $"anonRec{com.IncrementCounter():d}" Some(ident, arg), IdentExpr ident diff --git a/src/Fable.Transforms/Global/Naming.fs b/src/Fable.Transforms/Global/Naming.fs index 3310138db7..53a03effd2 100644 --- a/src/Fable.Transforms/Global/Naming.fs +++ b/src/Fable.Transforms/Global/Naming.fs @@ -443,11 +443,11 @@ module Naming = let reflectionSuffix = "_$reflection" let private printPart sanitize separator part overloadSuffix = - (if part = "" then + (if String.IsNullOrEmpty(part) then "" else separator + (sanitize part)) - + (if overloadSuffix = "" then + + (if String.IsNullOrEmpty(overloadSuffix) then "" else "_" + overloadSuffix) diff --git a/src/Fable.Transforms/Global/Prelude.fs b/src/Fable.Transforms/Global/Prelude.fs index 8a795b7c71..94a9aa486a 100644 --- a/src/Fable.Transforms/Global/Prelude.fs +++ b/src/Fable.Transforms/Global/Prelude.fs @@ -272,7 +272,7 @@ module Path = #endif let ChangeExtension (path: string, ext: string) = - let i = path.LastIndexOf(".") + let i = path.LastIndexOf(".", StringComparison.Ordinal) if i < 0 then path @@ -280,7 +280,7 @@ module Path = path.Substring(0, i) + ext let GetExtension (path: string) = - let i = path.LastIndexOf(".") + let i = path.LastIndexOf(".", StringComparison.Ordinal) if i < 0 then "" @@ -289,12 +289,12 @@ module Path = let GetFileName (path: string) = let normPath = normalizePath path - let i = normPath.LastIndexOf("/") + let i = normPath.LastIndexOf("/", StringComparison.Ordinal) normPath.Substring(i + 1) let GetFileNameWithoutExtension (path: string) = let filename = GetFileName path - let i = filename.LastIndexOf(".") + let i = filename.LastIndexOf(".", StringComparison.Ordinal) if i < 0 then filename @@ -303,7 +303,7 @@ module Path = let GetDirectoryName (path: string) = let normPath = normalizePath path - let i = normPath.LastIndexOf("/") + let i = normPath.LastIndexOf("/", StringComparison.Ordinal) if i < 0 then "" @@ -312,7 +312,7 @@ module Path = let GetDirectoryAndFileNames (path: string) = let normPath = normalizePath path - let i = normPath.LastIndexOf("/") + let i = normPath.LastIndexOf("/", StringComparison.Ordinal) if i < 0 then "", normPath diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 72c97651f2..7bada1b315 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -60,7 +60,7 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ let typeName = getTypeFastFullName genParams genArg if isStruct then - $"System.Nullable<{typeName}>" + $"System.Nullable<{typeName:s}>" else // there is no overload distinction for nullable reference types, // so the name and overload suffix are the same as the inner type @@ -130,7 +130,7 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ let genArgs = String.concat "," genArgs let genArgs = - if genArgs = "" then + if System.String.IsNullOrEmpty(genArgs) then "" else "[" + genArgs + "]" diff --git a/src/Fable.Transforms/Php/Fable2Php.fs b/src/Fable.Transforms/Php/Fable2Php.fs index cee3f10fdd..1bd460321f 100644 --- a/src/Fable.Transforms/Php/Fable2Php.fs +++ b/src/Fable.Transforms/Php/Fable2Php.fs @@ -1016,7 +1016,7 @@ let rec convertExpr (com: IPhpCompiler) (expr: Fable.Expr) = // convert calls on Array object by string key. // in Php $a['Hello'] is equivalent to $a->Hello, we choose this representation. - let meth = m.Substring(m.LastIndexOf(".") + 1) + let meth = m.Substring(m.LastIndexOf(".", StringComparison.Ordinal) + 1) PhpMethodCall(convertExpr com target, PhpIdent(unscopedIdent meth), convertArgs com (args)) diff --git a/src/Fable.Transforms/Python/Prelude.fs b/src/Fable.Transforms/Python/Prelude.fs index 963c209402..1fc2332a36 100644 --- a/src/Fable.Transforms/Python/Prelude.fs +++ b/src/Fable.Transforms/Python/Prelude.fs @@ -237,11 +237,11 @@ module Naming = name let private printPart sanitize separator part overloadSuffix = - (if part = "" then + (if String.IsNullOrEmpty(part) then "" else separator + (sanitize part)) - + (if overloadSuffix = "" then + + (if String.IsNullOrEmpty(overloadSuffix) then "" else "_" + overloadSuffix) diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index 3422c61d21..1b601fd127 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -1925,7 +1925,7 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex let value = value |> Option.defaultWith (fun () -> getZero com ctx t2) Helper.LibCall(com, "array", "create", t, [ size; value ]) | _ -> - $"Expecting an array type but got {t}" + $"Expecting an array type but got %A{t}" |> addErrorAndReturnNull com ctx.InlinePath r match i.CompiledName, args with @@ -3210,7 +3210,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth} doesn't support more than 2 arguments" + $"Regex.{meth:s} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4017,7 +4017,7 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | BclDateOnly | BclTimeOnly -> None diff --git a/src/Fable.Transforms/Replacements.Util.fs b/src/Fable.Transforms/Replacements.Util.fs index a2d937e0d2..8aa7a0645e 100644 --- a/src/Fable.Transforms/Replacements.Util.fs +++ b/src/Fable.Transforms/Replacements.Util.fs @@ -380,7 +380,7 @@ let getElementType = | _ -> Any let genericTypeInfoError (name: string) = - $"Cannot get type info of generic parameter {name}. Fable erases generics at runtime, try inlining the functions so generics can be resolved at compile time." + $"Cannot get type info of generic parameter {name:s}. Fable erases generics at runtime, try inlining the functions so generics can be resolved at compile time." // This is mainly intended for typeof errors because we want to show the user where the function is originally called let changeRangeToCallSite (inlinePath: InlinePath list) (range: SourceLocation option) = @@ -394,7 +394,7 @@ let splitFullName (fullname: string) = | -1 -> fullname | i -> fullname[.. i - 1] - match fullname.LastIndexOf(".") with + match fullname.LastIndexOf(".", StringComparison.Ordinal) with | -1 -> "", fullname | i -> fullname.Substring(0, i), fullname.Substring(i + 1) @@ -404,7 +404,7 @@ let getTypeNameFromFullName (fullname: string) = | -1 -> fullname | i -> fullname[.. i - 1] - match fullname.LastIndexOf(".") with + match fullname.LastIndexOf(".", StringComparison.Ordinal) with | -1 -> fullname | i -> fullname.Substring(i + 1) @@ -567,7 +567,7 @@ let partialApplyAtRuntime (com: Compiler) t arity (expr: Expr) (partialArgs: Exp | partialArgs -> curriedApply None t curried partialArgs | _ -> // Check if argTypes.Length < arity? - let makeArgIdent i typ = makeTypedIdent typ $"a{i}" // $"a{com.IncrementCounter()}$" + let makeArgIdent i typ = makeTypedIdent typ $"a{i:d}" // $"a{com.IncrementCounter()}$" let argTypes, returnType = uncurryLambdaType arity [] t let argIdents = argTypes |> List.mapi makeArgIdent let args = argIdents |> List.map Fable.IdentExpr @@ -636,7 +636,7 @@ let uncurryExprAtRuntime (com: Compiler) arity (expr: Expr) = | Fable.DelegateType(argTypes, returnType) -> argTypes, returnType | _ -> [], expr.Type - let makeArgIdent i typ = makeTypedIdent typ $"b{i}" // $"a{com.IncrementCounter()}$" + let makeArgIdent i typ = makeTypedIdent typ $"b{i:d}" // $"a{com.IncrementCounter()}$" let argIdents = argTypes |> List.mapi makeArgIdent let args = argIdents |> List.map Fable.IdentExpr let body = curriedApply None returnType expr args @@ -979,7 +979,7 @@ let (|UniversalFableCoreHelpers|_|) (com: ICompiler) (ctx: Context) r t (i: Call com ctx.InlinePath r - $"{i.CompiledName} is being compiled without replacement, this will fail at runtime." + $"{i.CompiledName:s} is being compiled without replacement, this will fail at runtime." let runtimeMsg = "A function supposed to be replaced by native code has been called, please check." @@ -1200,10 +1200,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Object doesn't contain field '{fieldName}' of type '{expectedType}' required by interface '{interfaceName}'" + $"Object doesn't contain field '{fieldName:s}' of type '{expectedType:s}' required by interface '{interfaceName:s}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Object doesn't contain field '{fieldName}' of any type [{expectedTypes}] required by interface '{interfaceName}'" + $"Object doesn't contain field '{fieldName:s}' of any type [{expectedTypes:s}] required by interface '{interfaceName:s}'" (range, fieldName, msg) @@ -1231,10 +1231,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType}' for field '{fieldName}' in interface '{interfaceName}', but is '{actualType}'" + $"Expected type '{expectedType:s}' for field '{fieldName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes}] for field '{fieldName}' in interface '{interfaceName}', but is '{actualType}'" + $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" | Some indexers -> assert (indexers |> List.isEmpty |> not) @@ -1247,10 +1247,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType}' for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'" + $"Expected type '{expectedType:s}' for field '{fieldName:s}' because of Indexer '{indexerName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'" + $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' because of Indexer '{indexerName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" | _ -> let indexerNames = indexers |> List.map (quote) |> String.concat "; " @@ -1258,10 +1258,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType}' for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'" + $"Expected type '{expectedType:s}' for field '{fieldName:s}' because of Indexers [{indexerNames:s}] in interface '{interfaceName:s}', but is '{actualType:s}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'" + $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' because of Indexers [{indexerNames:s}] in interface '{interfaceName:s}', but is '{actualType:s}'" let r = r |> Option.orElse range // fall back to anon record range diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index c71920a16d..6c0fe39401 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -920,7 +920,7 @@ let tryEntityIdent (com: Compiler) entFullName = | BuiltinDefinition(FSharpReference _) -> makeImportLib com Any "FSharpRef" "Types" |> Some | BuiltinDefinition(FSharpResult _) -> makeImportLib com Any "FSharpResult$2" "Result" |> Some | BuiltinDefinition(FSharpChoice genArgs) -> - let membName = $"FSharpChoice${List.length genArgs}" + let membName = $"FSharpChoice${List.length genArgs:d}" makeImportLib com Any membName "Choice" |> Some // | BuiltinDefinition BclGuid -> jsTypeof "string" expr // | BuiltinDefinition BclTimeSpan -> jsTypeof "number" expr @@ -3449,7 +3449,7 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opti | meth, Some thisArg -> let meth = if meth = "Next" then - $"Next{List.length args}" + $"Next{List.length args:d}" else meth @@ -3581,7 +3581,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth} doesn't support more than 2 arguments" + $"Regex.{meth:s} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4395,6 +4395,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs index 08888b656c..56f08f1e77 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs @@ -205,7 +205,7 @@ type System.String with | '\'' -> @"\'" | '\"' -> @"\""" | c when System.Char.IsControl(c) -> System.String.Format(@"\u{0}{1:x4}{2}", "{", int c, "}") - | c -> string c + | c -> string (c: char) ) else self @@ -225,7 +225,7 @@ type System.String with | '\'' -> @"\'" | '\"' -> @"\""" | c when c < '\x20' || c > '\x7e' -> System.String.Format(@"\u{0}{1:x4}{2}", "{", int c, "}") - | c -> string c + | c -> string (c: char) ) else self diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs index 72d5c6f8b5..835004a6d2 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs @@ -20,13 +20,17 @@ module Naming = let rustPrelude = HashSet(kw.RustPrelude) let rawIdent (ident: string) = - if ident = "" || ident = "_" || ident.StartsWith("r#") then + if + System.String.IsNullOrEmpty(ident) + || ident = "_" + || ident.StartsWith("r#", System.StringComparison.Ordinal) + then ident else "r#" + ident let stripRaw (ident: string) = - if ident.StartsWith("r#") then + if ident.StartsWith("r#", System.StringComparison.Ordinal) then ident.Substring("r#".Length) else ident @@ -191,14 +195,14 @@ module Literals = let mkBoolLit (value: bool) : Lit = { - token = mkBoolTokenLit ((string value).ToLowerInvariant()) + token = mkBoolTokenLit ((string (value: bool)).ToLowerInvariant()) kind = LitKind.Bool(value) span = DUMMY_SP } let mkCharLit (value: char) : Lit = { - token = mkCharTokenLit ((string value).escape_debug ()) + token = mkCharTokenLit ((string (value: char)).escape_debug ()) kind = LitKind.Char(value) span = DUMMY_SP } @@ -882,7 +886,10 @@ module Exprs = let mkEmitExpr (value: string) args : Expr = let value = // if value starts and ends with ", escape inside the quotes - if value.StartsWith("\"") && value.EndsWith("\"") then + if + value.StartsWith("\"", System.StringComparison.Ordinal) + && value.EndsWith("\"", System.StringComparison.Ordinal) + then "\"" + value[1 .. (value.Length - 2)].escape_debug () + "\"" else value diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs index b60c7f40cd..3d9ad35940 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs @@ -273,7 +273,7 @@ let print_emit_expr self value (args: Vec<_>, printArgs) = let i = int m.Groups[1].Value for j = i to args.Length - 1 do - rep.Add("$" + string j) + rep.Add("$" + string (j: int)) String.concat ", " rep ) diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 7891a9bd06..de8a6a79f2 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -1935,7 +1935,7 @@ module Util = sb.Append(List.head parts) |> ignore List.tail parts - |> List.iteri (fun i part -> sb.Append($"{{{i}}}" + part) |> ignore) + |> List.iteri (fun i part -> sb.Append($"{{{i:d}}}" + part) |> ignore) sb.ToString() @@ -2576,7 +2576,7 @@ module Util = match fableExpr with | Fable.IdentExpr ident when isArmScoped ctx ident.Name -> // if arm scoped, just output the ident value - let name = $"{ident.Name}_{0}_{0}" + let name = $"{ident.Name:s}_{0:d}_{0:d}" mkGenericPathExpr [ name ] None | _ -> // libCall com ctx range [] "Option" "getValue" [ fableExpr ] @@ -2592,7 +2592,7 @@ module Util = match fableExpr with | Fable.IdentExpr ident when isArmScoped ctx ident.Name -> // if ident is match arm scoped, just output the ident value - let name = $"{ident.Name}_{info.CaseIndex}_{info.FieldIndex}" + let name = $"{ident.Name:s}_{info.CaseIndex:d}_{info.FieldIndex:d}" mkGenericPathExpr [ name ] None | _ -> // Compile as: "match opt { MyUnion::Case(x, _) => x.clone() }" @@ -2966,7 +2966,7 @@ module Util = | 0 -> match nameOpt with | Some identName -> - let fieldName = $"{identName}_{caseIndex}_{0}" + let fieldName = $"{identName:s}_{caseIndex:d}_{0:d}" [ makeFullNameIdentPat fieldName ] | _ -> [ WILD_PAT ] | _ -> [] @@ -2989,7 +2989,7 @@ module Util = | Some identName -> unionCase.UnionCaseFields |> List.mapi (fun i _field -> - let fieldName = $"{identName}_{caseIndex}_{i}" + let fieldName = $"{identName:s}_{caseIndex:d}_{i:d}" makeFullNameIdentPat fieldName ) | _ -> unionCase.UnionCaseFields |> List.map (fun _field -> WILD_PAT) @@ -3044,7 +3044,7 @@ module Util = | Fable.Option(genArg, _) -> match evalName with | Some idName -> - let fieldName = $"{idName}_{caseIndex}_{0}" + let fieldName = $"{idName:s}_{caseIndex:d}_{0:d}" [ (fieldName, idName, genArg) ] | _ -> [] | Fable.DeclaredType(entRef, genArgs) -> @@ -3057,7 +3057,7 @@ module Util = | Some idName -> unionCase.UnionCaseFields |> List.mapi (fun i field -> - let fieldName = $"{idName}_{caseIndex}_{i}" + let fieldName = $"{idName:s}_{caseIndex:d}_{i:d}" let fieldType = FableTransforms.uncurryType field.FieldType (fieldName, idName, fieldType) ) @@ -3587,8 +3587,8 @@ module Util = let strBody = [ - $"let args = std::env::args().skip(1).map({asStr}).collect()" - $"{mainName}({asArr}(args))" + $"let args = std::env::args().skip(1).map({asStr:s}).collect()" + $"{mainName:s}({asArr:s}(args))" ] let fnBody = strBody |> Seq.map mkEmitSemiStmt |> mkBlock |> Some @@ -4584,7 +4584,7 @@ module Util = memb.CurriedParameterGroups |> List.collect id |> List.mapi (fun i p -> - let name = defaultArg p.Name $"arg{i}" + let name = defaultArg p.Name $"arg{i:d}" let typ = FableTransforms.uncurryType p.Type makeTypedIdent typ name ) @@ -5329,9 +5329,9 @@ module Compiler = import if isMacro then - $"{import.LocalIdent}!" + $"{import.LocalIdent:s}!" else - $"{import.LocalIdent}" + $"{import.LocalIdent:s}" member _.GetAllImports(ctx) = imports.Values diff --git a/src/Fable.Transforms/Rust/Replacements.fs b/src/Fable.Transforms/Rust/Replacements.fs index 58c16dcd03..39305d69d5 100644 --- a/src/Fable.Transforms/Rust/Replacements.fs +++ b/src/Fable.Transforms/Rust/Replacements.fs @@ -85,7 +85,10 @@ let makeStaticLibCall com r t (i: CallInfo) moduleName memberName args = let makeStaticMemberCall com r t (i: CallInfo) moduleName memberName args = let fullName = i.DeclaringEntityFullName - let entityName = fullName.Substring(fullName.LastIndexOf(".") + 1) + + let entityName = + fullName.Substring(fullName.LastIndexOf(".", StringComparison.Ordinal) + 1) + let memberName = entityName + "::" + memberName makeStaticLibCall com r t i moduleName memberName args @@ -152,7 +155,7 @@ let toLowerFirstWithArgsCountSuffix (args: Expr list) meth = let meth = Naming.lowerFirst meth if argCount > 1 then - meth + (string argCount) + meth + (string (argCount: int)) else meth @@ -323,7 +326,7 @@ let toSeq com t (expr: Expr) = Helper.LibCall(com, "Seq", "ofArray", t, [ chars ]) | _ -> TypeCast(expr, t) -let emitRawString (s: string) = $"\"{s}\"" |> emitExpr None String [] +let emitRawString (s: string) = $"\"{s:s}\"" |> emitExpr None String [] let emitFormat (com: ICompiler) r t (args: Expr list) macro = let args = @@ -766,7 +769,7 @@ let refCells (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op let getMemberName isStatic (i: CallInfo) = let memberName = i.CompiledName |> FSharp2Fable.Helpers.cleanNameAsRustIdentifier - if i.OverloadSuffix = "" then + if String.IsNullOrEmpty(i.OverloadSuffix) then memberName else let sep = @@ -851,7 +854,7 @@ let makeRustFormatString interpolated (fmt: string) = let g5 = m.Groups[5].Value let g4 = - if g4 = "" && (g5 = "f" || g5 = "F") then + if String.IsNullOrEmpty(g4) && (g5 = "f" || g5 = "F") then ".6" else g4 @@ -867,7 +870,7 @@ let makeRustFormatString interpolated (fmt: string) = let argFmt = let formatting = g2 + g3 + g4 + g5 - if formatting = "" then + if String.IsNullOrEmpty(formatting) then g1 + "{}" else g1 + "{:" + formatting + "}" @@ -2524,59 +2527,60 @@ let dateTimes (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o | ".ctor" -> match args with | [] -> "new_empty" |> Some - | ExprType(Number(Int64, _)) :: [] -> "new_ticks" |> Some - | ExprType(Number(Int64, _)) :: _kind :: [] -> "new_ticks_kind" |> Some - | ExprType(DeclaredType(ent, [])) :: _timeOnly :: [] when ent.FullName = Types.dateOnly -> - "new_date_time" |> Some - | ExprType(DeclaredType(ent, [])) :: _timeOnly :: _kind :: [] when ent.FullName = Types.dateOnly -> + | [ ExprType(Number(Int64, _)) ] -> "new_ticks" |> Some + | [ ExprType(Number(Int64, _)); _kind ] -> "new_ticks_kind" |> Some + | [ ExprType(DeclaredType(ent, [])); _timeOnly ] when ent.FullName = Types.dateOnly -> "new_date_time" |> Some + | [ ExprType(DeclaredType(ent, [])); _timeOnly; _kind ] when ent.FullName = Types.dateOnly -> "new_date_time_kind" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: [] -> - "new_ymd" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(_, - NumberInfo.IsEnum ent)) :: [] when - ent.FullName = "System.DateTimeKind" - -> + | [ ExprType(Number(Int32, _)); ExprType(Number(Int32, _)); ExprType(Number(Int32, _)) ] -> "new_ymd" |> Some + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(_, NumberInfo.IsEnum ent)) ] when ent.FullName = "System.DateTimeKind" -> "new_ymdhms_kind" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(_, - NumberInfo.IsEnum ent)) :: [] when - ent.FullName = "System.DateTimeKind" - -> + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(_, NumberInfo.IsEnum ent)) ] when ent.FullName = "System.DateTimeKind" -> "new_ymdhms_milli_kind" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(_, - NumberInfo.IsEnum ent)) :: [] when - ent.FullName = "System.DateTimeKind" - -> + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(_, NumberInfo.IsEnum ent)) ] when ent.FullName = "System.DateTimeKind" -> "new_ymdhms_micro_kind" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: [] -> - "new_ymdhms" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: [] -> - "new_ymdhms_milli" |> Some - | ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: ExprType(Number(Int32, - _)) :: [] -> - "new_ymdhms_micro" |> Some + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) ] -> "new_ymdhms" |> Some + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) ] -> "new_ymdhms_milli" |> Some + | [ ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) + ExprType(Number(Int32, _)) ] -> "new_ymdhms_micro" |> Some | _ -> None |> Option.map (fun meth -> makeStaticMemberCall com r t i "DateTime" meth args) | "Compare" @@ -2597,7 +2601,7 @@ let dateTimeOffsets (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: match args with | [] -> "new_empty" |> Some | ExprType(Number(Int64, _)) :: _ -> "new_ticks" |> Some - | ExprType(DeclaredType(ent, [])) :: [] when ent.FullName = Types.datetime -> "new_datetime" |> Some + | [ ExprType(DeclaredType(ent, [])) ] when ent.FullName = Types.datetime -> "new_datetime" |> Some | ExprType(DeclaredType(ent, [])) :: _ when ent.FullName = Types.datetime -> "new_datetime2" |> Some | ExprType(DeclaredType(ent, [])) :: _ when ent.FullName = Types.dateOnly -> "new_date_time" |> Some | [ ExprType(Number(Int32, _)) @@ -2733,7 +2737,7 @@ let timeSpans (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o | _ -> // overloads with variable argument counts let argCount = List.length args - let meth = meth + (string argCount) + let meth = meth + (string (argCount: int)) makeDateOrTimeMemberCall com ctx r t i "TimeSpan" meth thisArg args |> Some | meth -> makeDateOrTimeMemberCall com ctx r t i "TimeSpan" meth thisArg args |> Some @@ -3638,6 +3642,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/State.fs b/src/Fable.Transforms/State.fs index 014dbedb46..4408cf5f6c 100644 --- a/src/Fable.Transforms/State.fs +++ b/src/Fable.Transforms/State.fs @@ -47,7 +47,7 @@ type Assemblies(getPlugin, fsharpAssemblies: FSharpAssembly list, addLog: Severi // but keep the process going to mimic previous Fable behavior // and because these exception seems harmless let errorMessage = - $"Could not scan {path} for Fable plugins, skipping this assembly. Original error: {error.Message}" + $"Could not scan {path:s} for Fable plugins, skipping this assembly. Original error: {error.Message:s}" addLog Severity.Info errorMessage @@ -69,7 +69,7 @@ type Assemblies(getPlugin, fsharpAssemblies: FSharpAssembly list, addLog: Severi with ex -> let errorMessage = [ - $"Error while loading plugin: {e.FullName}" + $"Error while loading plugin: {e.FullName:s}" "" "This error often happens if you are trying to use a plugin that is not compatible with the current version of Fable." "If you see this error please open an issue at https://github.com/fable-compiler/Fable/" @@ -353,7 +353,7 @@ type CompilerImpl | Some r -> r, None | None -> let msg = - $"Cannot find root module for {fileName}. If this belongs to a package, make sure it includes the source files." + $"Cannot find root module for {fileName:s}. If this belongs to a package, make sure it includes the source files." (this :> Compiler).AddLog(msg, Severity.Warning, fileName = currentFile) diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs index c4f5c794b4..db4b50c744 100644 --- a/src/Fable.Transforms/Transforms.Util.fs +++ b/src/Fable.Transforms/Transforms.Util.fs @@ -1257,7 +1257,7 @@ module AST = | [] -> "" | head :: tail -> ((head, List.length args), tail) - ||> List.fold (fun (macro, pos) part -> $"{macro}$%i{pos}{part}", pos + 1) + ||> List.fold (fun (macro, pos) part -> $"{macro:s}$%i{pos}{part:s}", pos + 1) |> fst emit r t (args @ templateValues) isStatement macro @@ -1505,7 +1505,7 @@ module AST = let genArgsLength = List.length genArgs let genArgs = String.concat "," genArgs - $"System.{isStruct}Tuple`{genArgsLength}[{genArgs}]" + $"System.{isStruct:s}Tuple`{genArgsLength:d}[{genArgs:s}]" | Array(gen, _kind) -> // TODO: Check kind (getTypeFullName prettify gen) + "[]" @@ -1513,9 +1513,9 @@ module AST = let gen = getTypeFullName prettify gen if isStruct then - $"System.Nullable<{gen}>" + $"System.Nullable<{gen:s}>" else - $"{gen} | null" + $"{gen:s} | null" | Option(gen, isStruct) -> let gen = getTypeFullName prettify gen From 5df4c3861e9eefa5de65fb9233814d875573ccf2 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 23 Feb 2026 09:53:30 +0100 Subject: [PATCH 2/2] Revert interpolated string format specifiers and fix string type annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert all :s and :d format specifiers added to interpolated strings because Fable's Standalone tests compile the compiler to JavaScript, and Fable doesn't handle .NET-style format specifiers correctly — they produce literal % characters in the JS output. Also fix string type annotations to use string syntax instead of string (x: type), which the analyzer still flagged. Co-Authored-By: Claude Opus 4.6 --- src/Fable.Transforms/BabelPrinter.fs | 2 +- src/Fable.Transforms/Dart/Fable2Dart.fs | 12 +++++----- src/Fable.Transforms/Dart/Replacements.fs | 10 ++++---- src/Fable.Transforms/FSharp2Fable.Util.fs | 2 +- src/Fable.Transforms/FSharp2Fable.fs | 4 ++-- src/Fable.Transforms/Fable2Babel.fs | 19 +++++++-------- src/Fable.Transforms/FableTransforms.fs | 2 +- src/Fable.Transforms/OverloadSuffix.fs | 2 +- src/Fable.Transforms/Python/Replacements.fs | 8 +++---- src/Fable.Transforms/Replacements.Util.fs | 24 +++++++++---------- src/Fable.Transforms/Replacements.fs | 8 +++---- .../Rust/AST/Rust.AST.Adapters.fs | 4 ++-- .../Rust/AST/Rust.AST.Helpers.fs | 4 ++-- .../Rust/AST/Rust.AST.State.fs | 2 +- src/Fable.Transforms/Rust/Fable2Rust.fs | 24 +++++++++---------- src/Fable.Transforms/Rust/Replacements.fs | 8 +++---- src/Fable.Transforms/State.fs | 6 ++--- src/Fable.Transforms/Transforms.Util.fs | 8 +++---- 18 files changed, 74 insertions(+), 75 deletions(-) diff --git a/src/Fable.Transforms/BabelPrinter.fs b/src/Fable.Transforms/BabelPrinter.fs index 0825a02d89..62344d6801 100644 --- a/src/Fable.Transforms/BabelPrinter.fs +++ b/src/Fable.Transforms/BabelPrinter.fs @@ -447,7 +447,7 @@ module PrinterExtensions = |> List.iter ( function | _, NullOrUndefinedOrVoid -> () - | key, StringConstant value -> printProp (fun () -> printer.Print($"{key:s}=\"{value:s}\"")) + | key, StringConstant value -> printProp (fun () -> printer.Print($"{key}=\"{value}\"")) | key, value -> printProp (fun () -> printer.Print(key + "={") diff --git a/src/Fable.Transforms/Dart/Fable2Dart.fs b/src/Fable.Transforms/Dart/Fable2Dart.fs index 2b4bdd6a08..bd0169f731 100644 --- a/src/Fable.Transforms/Dart/Fable2Dart.fs +++ b/src/Fable.Transforms/Dart/Fable2Dart.fs @@ -178,7 +178,7 @@ module Util = match transformAndCaptureExpr com ctx entRef with | [], IdentExpression ident -> Some ident | _ -> - addError com [] None $"Unexpected, entity ref for {ent.FullName:s} is not an identifier" + addError com [] None $"Unexpected, entity ref for {ent.FullName} is not an identifier" None ) @@ -1953,17 +1953,17 @@ module Util = match transformCallArgs com ctx (CallInfo info) with | [], args -> args | _, args -> - $"Rewrite base arguments for {classDecl.Entity.FullName:s} so they can be compiled as Dart expressions" + $"Rewrite base arguments for {classDecl.Entity.FullName} so they can be compiled as Dart expressions" |> addWarning com [] e.Range args | Some(Fable.Value _ as e) -> - $"Ignoring base call for {classDecl.Entity.FullName:s}" + $"Ignoring base call for {classDecl.Entity.FullName}" |> addWarning com [] e.Range [] | Some e -> - $"Unexpected base call for {classDecl.Entity.FullName:s}" + $"Unexpected base call for {classDecl.Entity.FullName}" |> addError com [] e.Range [] @@ -2309,7 +2309,7 @@ module Util = let name = match p.Name with | Some name -> name - | None -> $"arg{i:d}$" + | None -> $"arg{i}$" let t = transformType com ctx p.Type FunctionArg(makeImmutableIdent t name) // TODO, isOptional=p.IsOptional, isNamed=p.IsNamed) @@ -2384,7 +2384,7 @@ module Util = = match implementsIterable, baseType with | Some iterable, Some _ -> - $"Types implementing IEnumerable cannot inherit from another class: {classEnt.FullName:s}" + $"Types implementing IEnumerable cannot inherit from another class: {classEnt.FullName}" |> addError com [] None Some iterable diff --git a/src/Fable.Transforms/Dart/Replacements.fs b/src/Fable.Transforms/Dart/Replacements.fs index 2b02c4e8bd..a99dfc9ef8 100644 --- a/src/Fable.Transforms/Dart/Replacements.fs +++ b/src/Fable.Transforms/Dart/Replacements.fs @@ -635,7 +635,7 @@ let tryEntityIdent (com: Compiler) entFullName = | BuiltinDefinition(FSharpReference _) -> makeImportLib com MetaType "FSharpRef" "Types" |> Some | BuiltinDefinition(FSharpResult _) -> makeImportLib com MetaType "FSharpResult$2" "Result" |> Some | BuiltinDefinition(FSharpChoice genArgs) -> - let membName = $"FSharpChoice${List.length genArgs:d}" + let membName = $"FSharpChoice${List.length genArgs}" makeImportLib com MetaType membName "Choice" |> Some // | BuiltinDefinition BclGuid -> jsTypeof "string" expr | BuiltinDefinition(BclHashSet _) @@ -1387,7 +1387,7 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt | "GetEnumerator", Some c, _ -> stringToCharSeq c |> getEnumerator com r t |> Some | ("Contains" | "StartsWith" | "EndsWith" as meth), Some c, arg :: _ -> if List.isMultiple args then - addWarning com ctx.InlinePath r $"String.{meth:s}: second argument is ignored" + addWarning com ctx.InlinePath r $"String.{meth}: second argument is ignored" Helper.InstanceCall(c, Naming.lowerFirst meth, t, [ arg ], ?loc = r) |> Some | ReplaceName [ "ToUpper", "toUpperCase" @@ -3106,7 +3106,7 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opti | meth, Some thisArg -> let meth = if meth = "Next" then - $"Next{List.length args:d}" + $"Next{List.length args}" else meth @@ -3221,7 +3221,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth:s} doesn't support more than 2 arguments" + $"Regex.{meth} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4137,6 +4137,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 03d1434385..4b8225569a 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -2530,7 +2530,7 @@ module Util = let failReplace (com: IFableCompiler) ctx r (info: Fable.ReplaceCallInfo) (thisArg: Fable.Expr option) = let msg = if info.DeclaringEntityFullName.StartsWith("Fable.Core.", StringComparison.Ordinal) then - $"{info.DeclaringEntityFullName:s}.{info.CompiledName:s} is not supported, try updating fable tool" + $"{info.DeclaringEntityFullName}.{info.CompiledName} is not supported, try updating fable tool" else com.WarnOnlyOnce( "Fable only supports a subset of standard .NET API, please check https://fable.io/docs/dotnet/compatibility.html. For external libraries, check whether they are Fable-compatible in the package docs." diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 5d1cbefd2a..18f55c831c 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -1648,7 +1648,7 @@ let private applyJsPyDecorators let parameters = memb.CurriedParameterGroups |> Seq.collect id - |> Seq.mapi (fun i p -> defaultArg p.Name $"arg{i:d}", makeType Map.empty p.Type) + |> Seq.mapi (fun i p -> defaultArg p.Name $"arg{i}", makeType Map.empty p.Type) |> Seq.toList Replacements.Api.makeMethodInfo com None name parameters returnType @@ -2614,7 +2614,7 @@ type FableCompiler(com: Compiler) = |> List.filter (fun (i, v) -> if ctx.CapturedBindings.Contains(i.Name) && canHaveSideEffects com v then if isIdentUsed i.Name resolved then - $"Inlined argument {i.Name:s} is being captured but is also used somewhere else. " + $"Inlined argument {i.Name} is being captured but is also used somewhere else. " + "There's a risk of double evaluation." |> addWarning com [] i.Range diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 4135122928..a4865fdbea 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -743,10 +743,10 @@ module Annotation = | Replacements.Util.FSharpMap(key, value) -> makeFableLibImportTypeAnnotation com ctx [ key; value ] "Map" "FSharpMap" | Replacements.Util.FSharpResult(ok, err) -> - $"FSharpResult$2{Util.UnionHelpers.UNION_SUFFIX:s}" + $"FSharpResult$2{Util.UnionHelpers.UNION_SUFFIX}" |> makeFableLibImportTypeAnnotation com ctx [ ok; err ] "Result" | Replacements.Util.FSharpChoice genArgs -> - $"FSharpChoice${List.length genArgs:d}{Util.UnionHelpers.UNION_SUFFIX:s}" + $"FSharpChoice${List.length genArgs}{Util.UnionHelpers.UNION_SUFFIX}" |> makeFableLibImportTypeAnnotation com ctx genArgs "Choice" | Replacements.Util.FSharpReference genArg -> if isInRefOrAnyType com typ then @@ -761,7 +761,7 @@ module Annotation = | _ -> argTypes |> List.mapi (fun i argType -> FunctionTypeParam.functionTypeParam ( - Identifier.identifier ($"arg{i:d}"), + Identifier.identifier ($"arg{i}"), makeTypeAnnotation com ctx argType ) ) @@ -1022,7 +1022,7 @@ module Util = if selector.StartsWith("*", StringComparison.Ordinal) then selector else - $"default as {selector:s}" + $"default as {selector}" com.GetImportExpr(ctx, selector, path, r, noMangle = true) |> ignore @@ -1402,7 +1402,7 @@ module Util = |> List.map (fun (a, p) -> match p.Name with | Some name when p.IsNamed && a.Name <> name -> - $"Argument {name:s} is marked as named but conflicts with another name in scope" + $"Argument {name} is marked as named but conflicts with another name in scope" |> addWarning com [] a.Range | _ -> () @@ -1612,7 +1612,7 @@ module Util = Expression.callExpression (helperRef, values, typeArguments = typeParams) | None -> callConstructor (Some case) | None -> - $"Unmatched union case tag: {tag:d} for {ent.FullName:s}" |> addWarning com [] r + $"Unmatched union case tag: {tag} for {ent.FullName}" |> addWarning com [] r callConstructor None else @@ -2600,8 +2600,7 @@ but thanks to the optimisation done below we get match List.tryItem tag ent.UnionCases with | Some case -> Some case.Name | None -> - $"Unmatched union case tag: {tag:d} for {ent.FullName:s}" - |> addWarning com [] range + $"Unmatched union case tag: {tag} for {ent.FullName}" |> addWarning com [] range None | _ -> None @@ -4083,7 +4082,7 @@ but thanks to the optimisation done below we get let genericSuffix = if ent.GenericParameters.Length > 0 then - $"`{ent.GenericParameters.Length:d}" + $"`{ent.GenericParameters.Length}" else "" @@ -4566,7 +4565,7 @@ but thanks to the optimisation done below we get let noConflict = ctx.UsedNames.RootScope.Add(alias) if not noConflict then - com.WarnOnlyOnce($"Import {alias:s} conflicts with existing identifier in root scope") + com.WarnOnlyOnce($"Import {alias} conflicts with existing identifier in root scope") alias else diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 2f75ff0c96..0504a57575 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -567,7 +567,7 @@ module private Transforms = match expr with | IdentExpr _ -> None, expr | arg -> - let ident = makeTypedIdent argType $"anonRec{com.IncrementCounter():d}" + let ident = makeTypedIdent argType $"anonRec{com.IncrementCounter()}" Some(ident, arg), IdentExpr ident diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 7bada1b315..9fc0f9308b 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -60,7 +60,7 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ let typeName = getTypeFastFullName genParams genArg if isStruct then - $"System.Nullable<{typeName:s}>" + $"System.Nullable<{typeName}>" else // there is no overload distinction for nullable reference types, // so the name and overload suffix are the same as the inner type diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index 1b601fd127..b9a84aa0d4 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -1925,7 +1925,7 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex let value = value |> Option.defaultWith (fun () -> getZero com ctx t2) Helper.LibCall(com, "array", "create", t, [ size; value ]) | _ -> - $"Expecting an array type but got %A{t}" + $"Expecting an array type but got {t}" |> addErrorAndReturnNull com ctx.InlinePath r match i.CompiledName, args with @@ -2497,7 +2497,7 @@ let intrinsicFunctions (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisAr Helper.ConstructorCall(constructor com ent, t, [], ?loc = r) |> Some | t -> - $"Cannot create instance of type unresolved at compile time: %A{t}" + $"Cannot create instance of type unresolved at compile time: {t}" |> addErrorAndReturnNull com ctx.InlinePath r |> Some // reference: https://msdn.microsoft.com/visualfsharpdocs/conceptual/operatorintrinsics.powdouble-function-%5bfsharp%5d @@ -3210,7 +3210,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth:s} doesn't support more than 2 arguments" + $"Regex.{meth} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4017,7 +4017,7 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | BclDateOnly | BclTimeOnly -> None diff --git a/src/Fable.Transforms/Replacements.Util.fs b/src/Fable.Transforms/Replacements.Util.fs index 8aa7a0645e..52d2f37777 100644 --- a/src/Fable.Transforms/Replacements.Util.fs +++ b/src/Fable.Transforms/Replacements.Util.fs @@ -380,7 +380,7 @@ let getElementType = | _ -> Any let genericTypeInfoError (name: string) = - $"Cannot get type info of generic parameter {name:s}. Fable erases generics at runtime, try inlining the functions so generics can be resolved at compile time." + $"Cannot get type info of generic parameter {name}. Fable erases generics at runtime, try inlining the functions so generics can be resolved at compile time." // This is mainly intended for typeof errors because we want to show the user where the function is originally called let changeRangeToCallSite (inlinePath: InlinePath list) (range: SourceLocation option) = @@ -567,7 +567,7 @@ let partialApplyAtRuntime (com: Compiler) t arity (expr: Expr) (partialArgs: Exp | partialArgs -> curriedApply None t curried partialArgs | _ -> // Check if argTypes.Length < arity? - let makeArgIdent i typ = makeTypedIdent typ $"a{i:d}" // $"a{com.IncrementCounter()}$" + let makeArgIdent i typ = makeTypedIdent typ $"a{i}" // $"a{com.IncrementCounter()}$" let argTypes, returnType = uncurryLambdaType arity [] t let argIdents = argTypes |> List.mapi makeArgIdent let args = argIdents |> List.map Fable.IdentExpr @@ -636,7 +636,7 @@ let uncurryExprAtRuntime (com: Compiler) arity (expr: Expr) = | Fable.DelegateType(argTypes, returnType) -> argTypes, returnType | _ -> [], expr.Type - let makeArgIdent i typ = makeTypedIdent typ $"b{i:d}" // $"a{com.IncrementCounter()}$" + let makeArgIdent i typ = makeTypedIdent typ $"b{i}" // $"a{com.IncrementCounter()}$" let argIdents = argTypes |> List.mapi makeArgIdent let args = argIdents |> List.map Fable.IdentExpr let body = curriedApply None returnType expr args @@ -979,7 +979,7 @@ let (|UniversalFableCoreHelpers|_|) (com: ICompiler) (ctx: Context) r t (i: Call com ctx.InlinePath r - $"{i.CompiledName:s} is being compiled without replacement, this will fail at runtime." + $"{i.CompiledName} is being compiled without replacement, this will fail at runtime." let runtimeMsg = "A function supposed to be replaced by native code has been called, please check." @@ -1200,10 +1200,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Object doesn't contain field '{fieldName:s}' of type '{expectedType:s}' required by interface '{interfaceName:s}'" + $"Object doesn't contain field '{fieldName}' of type '{expectedType}' required by interface '{interfaceName}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Object doesn't contain field '{fieldName:s}' of any type [{expectedTypes:s}] required by interface '{interfaceName:s}'" + $"Object doesn't contain field '{fieldName}' of any type [{expectedTypes}] required by interface '{interfaceName}'" (range, fieldName, msg) @@ -1231,10 +1231,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType:s}' for field '{fieldName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected type '{expectedType}' for field '{fieldName}' in interface '{interfaceName}', but is '{actualType}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected any type of [{expectedTypes}] for field '{fieldName}' in interface '{interfaceName}', but is '{actualType}'" | Some indexers -> assert (indexers |> List.isEmpty |> not) @@ -1247,10 +1247,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType:s}' for field '{fieldName:s}' because of Indexer '{indexerName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected type '{expectedType}' for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' because of Indexer '{indexerName:s}' in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'" | _ -> let indexerNames = indexers |> List.map (quote) |> String.concat "; " @@ -1258,10 +1258,10 @@ module AnonRecords = | [] -> unreachable () | [ expectedType ] -> let expectedType = expectedType |> formatType - $"Expected type '{expectedType:s}' for field '{fieldName:s}' because of Indexers [{indexerNames:s}] in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected type '{expectedType}' for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'" | _ -> let expectedTypes = expectedTypes |> formatTypes - $"Expected any type of [{expectedTypes:s}] for field '{fieldName:s}' because of Indexers [{indexerNames:s}] in interface '{interfaceName:s}', but is '{actualType:s}'" + $"Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'" let r = r |> Option.orElse range // fall back to anon record range diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 6c0fe39401..c71920a16d 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -920,7 +920,7 @@ let tryEntityIdent (com: Compiler) entFullName = | BuiltinDefinition(FSharpReference _) -> makeImportLib com Any "FSharpRef" "Types" |> Some | BuiltinDefinition(FSharpResult _) -> makeImportLib com Any "FSharpResult$2" "Result" |> Some | BuiltinDefinition(FSharpChoice genArgs) -> - let membName = $"FSharpChoice${List.length genArgs:d}" + let membName = $"FSharpChoice${List.length genArgs}" makeImportLib com Any membName "Choice" |> Some // | BuiltinDefinition BclGuid -> jsTypeof "string" expr // | BuiltinDefinition BclTimeSpan -> jsTypeof "number" expr @@ -3449,7 +3449,7 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opti | meth, Some thisArg -> let meth = if meth = "Next" then - $"Next{List.length args:d}" + $"Next{List.length args}" else meth @@ -3581,7 +3581,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp match thisArg, args with | Some thisArg, args -> if args.Length > 2 then - $"Regex.{meth:s} doesn't support more than 2 arguments" + $"Regex.{meth} doesn't support more than 2 arguments" |> addError com ctx.InlinePath r thisArg :: args |> Some @@ -4395,6 +4395,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs index 56f08f1e77..37277dd68f 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs @@ -205,7 +205,7 @@ type System.String with | '\'' -> @"\'" | '\"' -> @"\""" | c when System.Char.IsControl(c) -> System.String.Format(@"\u{0}{1:x4}{2}", "{", int c, "}") - | c -> string (c: char) + | c -> string c ) else self @@ -225,7 +225,7 @@ type System.String with | '\'' -> @"\'" | '\"' -> @"\""" | c when c < '\x20' || c > '\x7e' -> System.String.Format(@"\u{0}{1:x4}{2}", "{", int c, "}") - | c -> string (c: char) + | c -> string c ) else self diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs index 835004a6d2..49a695f434 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Helpers.fs @@ -195,14 +195,14 @@ module Literals = let mkBoolLit (value: bool) : Lit = { - token = mkBoolTokenLit ((string (value: bool)).ToLowerInvariant()) + token = mkBoolTokenLit ((string value).ToLowerInvariant()) kind = LitKind.Bool(value) span = DUMMY_SP } let mkCharLit (value: char) : Lit = { - token = mkCharTokenLit ((string (value: char)).escape_debug ()) + token = mkCharTokenLit ((string value).escape_debug ()) kind = LitKind.Char(value) span = DUMMY_SP } diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs index 3d9ad35940..086d21cae0 100644 --- a/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs +++ b/src/Fable.Transforms/Rust/AST/Rust.AST.State.fs @@ -273,7 +273,7 @@ let print_emit_expr self value (args: Vec<_>, printArgs) = let i = int m.Groups[1].Value for j = i to args.Length - 1 do - rep.Add("$" + string (j: int)) + rep.Add("$" + string j) String.concat ", " rep ) diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index de8a6a79f2..7891a9bd06 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -1935,7 +1935,7 @@ module Util = sb.Append(List.head parts) |> ignore List.tail parts - |> List.iteri (fun i part -> sb.Append($"{{{i:d}}}" + part) |> ignore) + |> List.iteri (fun i part -> sb.Append($"{{{i}}}" + part) |> ignore) sb.ToString() @@ -2576,7 +2576,7 @@ module Util = match fableExpr with | Fable.IdentExpr ident when isArmScoped ctx ident.Name -> // if arm scoped, just output the ident value - let name = $"{ident.Name:s}_{0:d}_{0:d}" + let name = $"{ident.Name}_{0}_{0}" mkGenericPathExpr [ name ] None | _ -> // libCall com ctx range [] "Option" "getValue" [ fableExpr ] @@ -2592,7 +2592,7 @@ module Util = match fableExpr with | Fable.IdentExpr ident when isArmScoped ctx ident.Name -> // if ident is match arm scoped, just output the ident value - let name = $"{ident.Name:s}_{info.CaseIndex:d}_{info.FieldIndex:d}" + let name = $"{ident.Name}_{info.CaseIndex}_{info.FieldIndex}" mkGenericPathExpr [ name ] None | _ -> // Compile as: "match opt { MyUnion::Case(x, _) => x.clone() }" @@ -2966,7 +2966,7 @@ module Util = | 0 -> match nameOpt with | Some identName -> - let fieldName = $"{identName:s}_{caseIndex:d}_{0:d}" + let fieldName = $"{identName}_{caseIndex}_{0}" [ makeFullNameIdentPat fieldName ] | _ -> [ WILD_PAT ] | _ -> [] @@ -2989,7 +2989,7 @@ module Util = | Some identName -> unionCase.UnionCaseFields |> List.mapi (fun i _field -> - let fieldName = $"{identName:s}_{caseIndex:d}_{i:d}" + let fieldName = $"{identName}_{caseIndex}_{i}" makeFullNameIdentPat fieldName ) | _ -> unionCase.UnionCaseFields |> List.map (fun _field -> WILD_PAT) @@ -3044,7 +3044,7 @@ module Util = | Fable.Option(genArg, _) -> match evalName with | Some idName -> - let fieldName = $"{idName:s}_{caseIndex:d}_{0:d}" + let fieldName = $"{idName}_{caseIndex}_{0}" [ (fieldName, idName, genArg) ] | _ -> [] | Fable.DeclaredType(entRef, genArgs) -> @@ -3057,7 +3057,7 @@ module Util = | Some idName -> unionCase.UnionCaseFields |> List.mapi (fun i field -> - let fieldName = $"{idName:s}_{caseIndex:d}_{i:d}" + let fieldName = $"{idName}_{caseIndex}_{i}" let fieldType = FableTransforms.uncurryType field.FieldType (fieldName, idName, fieldType) ) @@ -3587,8 +3587,8 @@ module Util = let strBody = [ - $"let args = std::env::args().skip(1).map({asStr:s}).collect()" - $"{mainName:s}({asArr:s}(args))" + $"let args = std::env::args().skip(1).map({asStr}).collect()" + $"{mainName}({asArr}(args))" ] let fnBody = strBody |> Seq.map mkEmitSemiStmt |> mkBlock |> Some @@ -4584,7 +4584,7 @@ module Util = memb.CurriedParameterGroups |> List.collect id |> List.mapi (fun i p -> - let name = defaultArg p.Name $"arg{i:d}" + let name = defaultArg p.Name $"arg{i}" let typ = FableTransforms.uncurryType p.Type makeTypedIdent typ name ) @@ -5329,9 +5329,9 @@ module Compiler = import if isMacro then - $"{import.LocalIdent:s}!" + $"{import.LocalIdent}!" else - $"{import.LocalIdent:s}" + $"{import.LocalIdent}" member _.GetAllImports(ctx) = imports.Values diff --git a/src/Fable.Transforms/Rust/Replacements.fs b/src/Fable.Transforms/Rust/Replacements.fs index 39305d69d5..47ad85bf73 100644 --- a/src/Fable.Transforms/Rust/Replacements.fs +++ b/src/Fable.Transforms/Rust/Replacements.fs @@ -155,7 +155,7 @@ let toLowerFirstWithArgsCountSuffix (args: Expr list) meth = let meth = Naming.lowerFirst meth if argCount > 1 then - meth + (string (argCount: int)) + meth + (string argCount) else meth @@ -326,7 +326,7 @@ let toSeq com t (expr: Expr) = Helper.LibCall(com, "Seq", "ofArray", t, [ chars ]) | _ -> TypeCast(expr, t) -let emitRawString (s: string) = $"\"{s:s}\"" |> emitExpr None String [] +let emitRawString (s: string) = $"\"{s}\"" |> emitExpr None String [] let emitFormat (com: ICompiler) r t (args: Expr list) macro = let args = @@ -2737,7 +2737,7 @@ let timeSpans (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o | _ -> // overloads with variable argument counts let argCount = List.length args - let meth = meth + (string (argCount: int)) + let meth = meth + (string argCount) makeDateOrTimeMemberCall com ctx r t i "TimeSpan" meth thisArg args |> Some | meth -> makeDateOrTimeMemberCall com ctx r t i "TimeSpan" meth thisArg args |> Some @@ -3642,6 +3642,6 @@ let tryType typ = | FSharpMap(key, value) -> Some(Types.fsharpMap, maps, [ key; value ]) | FSharpSet genArg -> Some(Types.fsharpSet, sets, [ genArg ]) | FSharpResult(genArg1, genArg2) -> Some(Types.result, results, [ genArg1; genArg2 ]) - | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric:s}`{List.length genArgs:d}", results, genArgs) + | FSharpChoice genArgs -> Some($"{Types.choiceNonGeneric}`{List.length genArgs}", results, genArgs) | FSharpReference genArg -> Some(Types.refCell, refCells, [ genArg ]) | _ -> None diff --git a/src/Fable.Transforms/State.fs b/src/Fable.Transforms/State.fs index 4408cf5f6c..014dbedb46 100644 --- a/src/Fable.Transforms/State.fs +++ b/src/Fable.Transforms/State.fs @@ -47,7 +47,7 @@ type Assemblies(getPlugin, fsharpAssemblies: FSharpAssembly list, addLog: Severi // but keep the process going to mimic previous Fable behavior // and because these exception seems harmless let errorMessage = - $"Could not scan {path:s} for Fable plugins, skipping this assembly. Original error: {error.Message:s}" + $"Could not scan {path} for Fable plugins, skipping this assembly. Original error: {error.Message}" addLog Severity.Info errorMessage @@ -69,7 +69,7 @@ type Assemblies(getPlugin, fsharpAssemblies: FSharpAssembly list, addLog: Severi with ex -> let errorMessage = [ - $"Error while loading plugin: {e.FullName:s}" + $"Error while loading plugin: {e.FullName}" "" "This error often happens if you are trying to use a plugin that is not compatible with the current version of Fable." "If you see this error please open an issue at https://github.com/fable-compiler/Fable/" @@ -353,7 +353,7 @@ type CompilerImpl | Some r -> r, None | None -> let msg = - $"Cannot find root module for {fileName:s}. If this belongs to a package, make sure it includes the source files." + $"Cannot find root module for {fileName}. If this belongs to a package, make sure it includes the source files." (this :> Compiler).AddLog(msg, Severity.Warning, fileName = currentFile) diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs index db4b50c744..c4f5c794b4 100644 --- a/src/Fable.Transforms/Transforms.Util.fs +++ b/src/Fable.Transforms/Transforms.Util.fs @@ -1257,7 +1257,7 @@ module AST = | [] -> "" | head :: tail -> ((head, List.length args), tail) - ||> List.fold (fun (macro, pos) part -> $"{macro:s}$%i{pos}{part:s}", pos + 1) + ||> List.fold (fun (macro, pos) part -> $"{macro}$%i{pos}{part}", pos + 1) |> fst emit r t (args @ templateValues) isStatement macro @@ -1505,7 +1505,7 @@ module AST = let genArgsLength = List.length genArgs let genArgs = String.concat "," genArgs - $"System.{isStruct:s}Tuple`{genArgsLength:d}[{genArgs:s}]" + $"System.{isStruct}Tuple`{genArgsLength}[{genArgs}]" | Array(gen, _kind) -> // TODO: Check kind (getTypeFullName prettify gen) + "[]" @@ -1513,9 +1513,9 @@ module AST = let gen = getTypeFullName prettify gen if isStruct then - $"System.Nullable<{gen:s}>" + $"System.Nullable<{gen}>" else - $"{gen:s} | null" + $"{gen} | null" | Option(gen, isStruct) -> let gen = getTypeFullName prettify gen