diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f04fed4..fd35bc449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migrate from `Paket` to `Directory.Packages.props` #722 [@xperiandri] - Migrate to .NET `9.0.201` and FCS `43.9.201` #722 [@xperiandri] - Write test logs to test context output #722 [@xperiandri] +- Use string interpolation instead of `+` concatenation #724 [@xperiandri] ## [0.24.2] - 2024-02-29 diff --git a/build.fsx b/build.fsx index 9f71f945e..1942bc876 100644 --- a/build.fsx +++ b/build.fsx @@ -52,8 +52,8 @@ let authors = "Matthew Mcveigh" let gitOwner = "fsprojects" let gitName = "FSharpLint" -let gitHome = "https://github.com/" + gitOwner -let gitUrl = gitHome + "/" + gitName +let gitHome = $"https://github.com/{gitOwner}" +let gitUrl = $"{gitHome}/{gitName}" // -------------------------------------------------------------------------------------- // Helpers diff --git a/docs/generators/apiref.fsx b/docs/generators/apiref.fsx index ca8262234..559dbb13d 100644 --- a/docs/generators/apiref.fsx +++ b/docs/generators/apiref.fsx @@ -78,10 +78,10 @@ let generateType ctx (page: ApiPageInfo) = div [Class "api-page"] [ h2 [] [!! t.Name] b [] [!! "Namespace: "] - a [Href (sprintf "%s.html" page.NamespaceUrlName) ] [!! page.NamespaceName] + a [Href ($"%s{page.NamespaceUrlName}.html")] [!! page.NamespaceName] br [] b [] [!! "Parent: "] - a [Href (sprintf "%s.html" page.ParentUrlName)] [!! page.ParentName] + a [Href ($"%s{page.ParentUrlName}.html")] [!! page.ParentName] span [] [!! (getComment t.Comment)] br [] if not (String.IsNullOrWhiteSpace t.Category) then @@ -127,10 +127,10 @@ let generateModule ctx (page: ApiPageInfo) = div [Class "api-page"] [ h2 [] [!!m.Name] b [] [!! "Namespace: "] - a [Href (sprintf "%s.html" page.NamespaceUrlName) ] [!! page.NamespaceName] + a [Href ($"%s{page.NamespaceUrlName}.html")] [!! page.NamespaceName] br [] b [] [!! "Parent: "] - a [Href (sprintf "%s.html" page.ParentUrlName)] [!! page.ParentName] + a [Href ($"%s{page.ParentUrlName}.html")] [!! page.ParentName] span [] [!! (getComment m.Comment)] br [] if not (String.IsNullOrWhiteSpace m.Category) then @@ -147,7 +147,7 @@ let generateModule ctx (page: ApiPageInfo) = ] for t in m.NestedTypes do tr [] [ - td [] [a [Href (sprintf "%s.html" t.UrlName )] [!! t.Name ]] + td [] [a [Href ($"%s{t.UrlName}.html")] [!! t.Name ]] td [] [!! (getComment t.Comment)] ] ] @@ -162,7 +162,7 @@ let generateModule ctx (page: ApiPageInfo) = ] for t in m.NestedModules do tr [] [ - td [] [a [Href (sprintf "%s.html" t.UrlName )] [!! t.Name ]] + td [] [a [Href ($"%s{t.UrlName}.html")] [!! t.Name ]] td [] [!! (getComment t.Comment)] ] ] @@ -206,7 +206,7 @@ let generateNamespace ctx (n: Namespace) = ] for t in n.Types do tr [] [ - td [] [a [Href (sprintf "%s.html" t.UrlName )] [!! t.Name ]] + td [] [a [Href ($"%s{t.UrlName}.html")] [!! t.Name ]] td [] [!!(getComment t.Comment)] ] ] @@ -222,7 +222,7 @@ let generateNamespace ctx (n: Namespace) = ] for t in n.Modules do tr [] [ - td [] [a [Href (sprintf "%s.html" t.UrlName )] [!! t.Name ]] + td [] [a [Href ($"%s{t.UrlName}.html")] [!! t.Name ]] td [] [!! (getComment t.Comment)] ] ] @@ -257,12 +257,12 @@ let generate' (ctx : SiteContents) = b [] [!! "Declared namespaces"] br [] for (n, _) in namespaces do - a [Href (sprintf "%s.html" n)] [!!n] + a [Href ($"%s{n}.html")] [!!n] br [] ] n.Label [("index" , ref); yield! namespaces; yield! modules; yield! types] - |> List.map (fun (x, y) -> (sprintf "%s/%s" n.Label x), y) + |> List.map (fun (x, y) -> $"%s{n.Label}/%s{x}", y) ) diff --git a/docs/generators/lunr.fsx b/docs/generators/lunr.fsx index eaceafd3f..a38fd9e55 100644 --- a/docs/generators/lunr.fsx +++ b/docs/generators/lunr.fsx @@ -42,8 +42,8 @@ let generate (ctx : SiteContents) (projectRoot: string) (page: string) = let gen = let ctn = - sprintf "%s \n %s" generatorOutput.AssemblyGroup.Name (generatorOutput.AssemblyGroup.Namespaces |> Seq.map (fun n -> n.Name) |> String.concat " ") - {uri = (rootUrl + sprintf "/reference/%s/index.html" n.Label ); title = sprintf "%s - API Reference" n.Label; content = ctn } + $"%s{generatorOutput.AssemblyGroup.Name} \n %s{(generatorOutput.AssemblyGroup.Namespaces |> Seq.map (fun n -> n.Name) |> String.concat " ")}" + {uri = $"{rootUrl}/reference/%s{n.Label}/index.html"; title = $"%s{n.Label} - API Reference"; content = ctn } let mdlsGen = allModules @@ -59,7 +59,7 @@ let generate (ctx : SiteContents) (projectRoot: string) (page: string) = (m.TypeExtensions |> List.map (fun m -> m.Name + " " + m.Comment.FullText ) |> String.concat " ") - {uri = rootUrl + sprintf "/reference/%s/%s.html" n.Label m.UrlName ; title = m.Name; content = cnt } + {uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlName}.html"; title = m.Name; content = cnt } ) let tsGen = @@ -67,13 +67,11 @@ let generate (ctx : SiteContents) (projectRoot: string) (page: string) = |> Seq.map (fun m -> let m = m.Info let cnt = - sprintf "%s \n %s \n %s" - m.Name - m.Comment.FullText - (m.AllMembers |> List.map (fun m -> m.Name + " " + m.Comment.FullText ) |> String.concat " ") + let allMembers = m.AllMembers |> List.map (fun m -> $"{m.Name} {m.Comment.FullText}" ) |> String.concat " " + $"%s{m.Name} \n %s{m.Comment.FullText} \n %s{allMembers}" - {uri = rootUrl + sprintf "/reference/%s/%s.html" n.Label m.UrlName ; title = m.Name; content = cnt } + {uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlName}.html"; title = m.Name; content = cnt } ) [yield! entries; gen; yield! mdlsGen; yield! tsGen] ) diff --git a/docs/generators/partials/header.fsx b/docs/generators/partials/header.fsx index 0b06c2d00..be7ad2e7e 100644 --- a/docs/generators/partials/header.fsx +++ b/docs/generators/partials/header.fsx @@ -27,6 +27,6 @@ let header (ctx : SiteContents) page = link [Rel "stylesheet"; Href (rootUrl + "/static/css/custom.css")] link [Rel "stylesheet"; Href "//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/atom-one-dark.min.css"] if siteInfo.theme_variant.IsSome then - link [Rel "stylesheet"; Href (rootUrl + (sprintf "/static/css/theme-%s.css" siteInfo.theme_variant.Value))] + link [Rel "stylesheet"; Href (rootUrl + ($"/static/css/theme-%s{siteInfo.theme_variant.Value}.css"))] script [Src (rootUrl + "/static/js/jquery-3.3.1.min.js")] [] ] diff --git a/docs/generators/partials/menu.fsx b/docs/generators/partials/menu.fsx index cef00bf3d..26a056f5c 100644 --- a/docs/generators/partials/menu.fsx +++ b/docs/generators/partials/menu.fsx @@ -91,7 +91,7 @@ let menu (ctx : SiteContents) (page: string) = ] script [Type "text/javascript"; Src (rootUrl + "/static/js/lunr.min.js")] [] script [Type "text/javascript"; Src (rootUrl + "/static/js/auto-complete.js")] [] - script [Type "text/javascript";] [!! (sprintf "var baseurl ='%s'" rootUrl)] + script [Type "text/javascript";] [!! ($"var baseurl ='%s{rootUrl}'")] script [Type "text/javascript"; Src (rootUrl + "/static/js/search.js")] [] ] div [Class "highlightable"] [ diff --git a/src/FSharpLint.Console/Output.fs b/src/FSharpLint.Console/Output.fs index dac7be9a8..b0b48f071 100644 --- a/src/FSharpLint.Console/Output.fs +++ b/src/FSharpLint.Console/Output.fs @@ -25,7 +25,7 @@ type StandardOutput () = errorLine |> Seq.mapi (fun i _ -> if i = range.StartColumn then "^" else " ") |> Seq.reduce (+) - getErrorMessage range + Environment.NewLine + errorLine + Environment.NewLine + highlightColumnLine + $"{getErrorMessage range}{Environment.NewLine}{errorLine}{Environment.NewLine}{highlightColumnLine}" let writeLine (str:string) (color:ConsoleColor) (writer:IO.TextWriter) = let originalColour = Console.ForegroundColor @@ -34,21 +34,23 @@ type StandardOutput () = Console.ForegroundColor <- originalColour interface IOutput with + member _.WriteInfo (info:string) = writeLine info ConsoleColor.White Console.Out member this.WriteWarning (warning:Suggestion.LintWarning) = let highlightedErrorText = highlightErrorText warning.Details.Range warning.ErrorText - let ruleUrlHint = sprintf "See https://fsprojects.github.io/FSharpLint/how-tos/rules/%s.html" warning.RuleIdentifier - let str = warning.Details.Message + Environment.NewLine + highlightedErrorText - + Environment.NewLine + ruleUrlHint + let ruleUrlHint = $"See https://fsprojects.github.io/FSharpLint/how-tos/rules/%s{warning.RuleIdentifier}.html" + let str = $"{warning.Details.Message}{Environment.NewLine}{highlightedErrorText}{Environment.NewLine}{ruleUrlHint}" writeLine str ConsoleColor.Yellow Console.Out String.replicate 80 "-" |> (this :> IOutput).WriteInfo member _.WriteError (error:string) = writeLine error ConsoleColor.Red Console.Error type MSBuildOutput () = + interface IOutput with + member _.WriteInfo (info:string) = Console.Out.WriteLine info member _.WriteWarning (warning:Suggestion.LintWarning) = - sprintf "%s(%d,%d,%d,%d):FSharpLint warning %s: %s" + fprintf Console.Out "%s(%d,%d,%d,%d):FSharpLint warning %s: %s" <| warning.FilePath <| warning.Details.Range.StartLine <| warning.Details.Range.StartColumn @@ -56,7 +58,6 @@ type MSBuildOutput () = <| warning.Details.Range.EndColumn <| warning.RuleIdentifier <| warning.Details.Message - |> Console.Out.WriteLine member _.WriteError (error:string) = - sprintf "FSharpLint error: %s" error + $"FSharpLint error: {error}" |> Console.Error.WriteLine \ No newline at end of file diff --git a/src/FSharpLint.Console/Program.fs b/src/FSharpLint.Console/Program.fs index 73d43cd43..85174ca67 100644 --- a/src/FSharpLint.Console/Program.fs +++ b/src/FSharpLint.Console/Program.fs @@ -24,7 +24,7 @@ type private FileType = type private ToolArgs = | [] Format of OutputFormat | [] Lint of ParseResults - | Version + | Version with interface IArgParserTemplate with member this.Usage = @@ -56,10 +56,7 @@ let private parserProgress (output:Output.IOutput) = function String.Format(Resources.GetString("ConsoleFinishedFile"), List.length warnings) |> output.WriteInfo | Failed (file, parseException) -> String.Format(Resources.GetString("ConsoleFailedToParseFile"), file) |> output.WriteError - "Exception Message:" + Environment.NewLine + - parseException.Message + Environment.NewLine + - "Exception Stack Trace:" + Environment.NewLine + - parseException.StackTrace + Environment.NewLine + $"Exception Message:{Environment.NewLine}{parseException.Message}{Environment.NewLine}Exception Stack Trace:{Environment.NewLine}{parseException.StackTrace}{Environment.NewLine}" |> output.WriteError /// Infers the file type of the target based on its file extension. @@ -84,10 +81,10 @@ let private start (arguments:ParseResults) (toolsPath:Ionide.ProjInfo. | None -> Output.StandardOutput() :> Output.IOutput if arguments.Contains ToolArgs.Version then - let version = + let version = Assembly.GetExecutingAssembly().GetCustomAttributes false |> Seq.pick (function | :? AssemblyInformationalVersionAttribute as aiva -> Some aiva.InformationalVersion | _ -> None) - sprintf "Current version: %s" version |> output.WriteInfo + $"Current version: {version}" |> output.WriteInfo () let handleError (str:string) = @@ -134,12 +131,12 @@ let private start (arguments:ParseResults) (toolsPath:Ionide.ProjInfo. with | e -> let target = if fileType = FileType.Source then "source" else target - sprintf "Lint failed while analysing %s.\nFailed with: %s\nStack trace: %s" target e.Message e.StackTrace + $"Lint failed while analysing %s{target}.{Environment.NewLine}Failed with: %s{e.Message}{Environment.NewLine}Stack trace: {e.StackTrace}" |> handleError | _ -> () exitCode - + /// Must be called only once per process. /// We're calling it globally so we can call main multiple times from our tests. let toolsPath = Ionide.ProjInfo.Init.init (DirectoryInfo <| Directory.GetCurrentDirectory()) None diff --git a/src/FSharpLint.Core/Application/Configuration.fs b/src/FSharpLint.Core/Application/Configuration.fs index c1f1a8501..52d7098c3 100644 --- a/src/FSharpLint.Core/Application/Configuration.fs +++ b/src/FSharpLint.Core/Application/Configuration.fs @@ -64,7 +64,7 @@ module IgnoreFiles = let parseIgnorePath (path:string) = let globToRegex glob = Regex( - "^" + Regex.Escape(glob).Replace(@"\*", ".*").Replace(@"\?", ".") + "$", + $"""^{Regex.Escape(glob).Replace(@"\*", ".*").Replace(@"\?", ".")}$""", RegexOptions.IgnoreCase ||| RegexOptions.Singleline) let isDirectory = path.EndsWith("/") @@ -584,7 +584,7 @@ let parseConfig (configText:string) = try JsonConvert.DeserializeObject(configText, FSharpJsonConverter.serializerSettings) with - | ex -> raise <| ConfigurationException(sprintf "Couldn't parse config, error=%s" ex.Message) + | ex -> raise <| ConfigurationException $"Couldn't parse config, error=%s{ex.Message}" /// Tries to parse the config file at the provided path. let loadConfig (configPath:string) = @@ -628,7 +628,7 @@ let private parseHints (hints:string []) = match FParsec.CharParsers.run HintParser.phint hint with | FParsec.CharParsers.Success(hint, _, _) -> hint | FParsec.CharParsers.Failure(error, _, _) -> - raise <| ConfigurationException("Failed to parse hint: " + hint + "\n" + error) + raise <| ConfigurationException $"Failed to parse hint: {hint}{Environment.NewLine}{error}" hints |> Array.filter (System.String.IsNullOrWhiteSpace >> not) diff --git a/src/FSharpLint.Core/Application/Lint.fs b/src/FSharpLint.Core/Application/Lint.fs index de7059697..2ad5e7084 100644 --- a/src/FSharpLint.Core/Application/Lint.fs +++ b/src/FSharpLint.Core/Application/Lint.fs @@ -61,7 +61,7 @@ module Lint = let getParseFailureReason = function | ParseFile.FailedToParseFile failures -> let getFailureReason (x:FSharpDiagnostic) = - sprintf "failed to parse file %s, message: %s" x.FileName x.Message + $"failed to parse file {x.FileName}, message: {x.Message}" String.Join(", ", failures |> Array.map getFailureReason) | ParseFile.AbortedTypeCheck -> "Type check failed. You might want to build your solution/project first and try again." @@ -78,10 +78,10 @@ module Lint = | RunTimeConfigError error -> String.Format(Resources.GetString("ConsoleRunTimeConfigError"), error) | FailedToParseFile failure -> - "Lint failed to parse a file. Failed with: " + getParseFailureReason failure + $"Lint failed to parse a file. Failed with: {getParseFailureReason failure}" | FailedToParseFilesInProject failures -> let failureReasons = String.Join("\n", failures |> List.map getParseFailureReason) - "Lint failed to parse files. Failed with: " + failureReasons + $"Lint failed to parse files. Failed with: {failureReasons}" [] type Result<'T> = diff --git a/src/FSharpLint.Core/Framework/AbstractSyntaxArray.fs b/src/FSharpLint.Core/Framework/AbstractSyntaxArray.fs index 02b017917..a1715ad15 100644 --- a/src/FSharpLint.Core/Framework/AbstractSyntaxArray.fs +++ b/src/FSharpLint.Core/Framework/AbstractSyntaxArray.fs @@ -131,18 +131,18 @@ module AbstractSyntaxArray = | TypeRepresentation(_) | File(_) | LambdaArg(_) - | LambdaBody(_) - | Else(_) + | LambdaBody(_) + | Else(_) | ComponentInfo(_) -> SyntaxNode.Other | EnumCase(_) -> SyntaxNode.EnumCase | UnionCase(_) -> SyntaxNode.UnionCase - + [] type TempNode(hashcode: int, actual: AstNode) = member _.Hashcode = hashcode member _.Actual = actual - - member private _.DebuggerDisplay = "AstNode: " + string actual + + member private _.DebuggerDisplay = $"AstNode: {string actual}" [] type Node = @@ -248,7 +248,7 @@ module AbstractSyntaxArray = let depth = stackedNode.Depth tryAddPossibleSkips depth - + // Strip out "extra info". let node = let extractExtraInfo actual extraInfoNode = @@ -279,7 +279,7 @@ module AbstractSyntaxArray = let skip = skips.[i] let node = nodes.[skip.Index] - result.[skip.Index] <- + result.[skip.Index] <- { Hashcode = node.Hashcode Actual = node.Actual NumberOfChildren = skip.NumberOfChildren diff --git a/src/FSharpLint.Core/Framework/HintParser.fs b/src/FSharpLint.Core/Framework/HintParser.fs index 75c0bc244..210b03e69 100644 --- a/src/FSharpLint.Core/Framework/HintParser.fs +++ b/src/FSharpLint.Core/Framework/HintParser.fs @@ -428,7 +428,7 @@ module HintParser = let isKeyword = List.exists ((=) identStr) FSharpKeywords.KeywordNames - if isKeyword then fail (sprintf "Unexpected keyword %s" identStr) + if isKeyword then fail $"Unexpected keyword %s{identStr}" else preturn ident let private pident: (CharStream -> Reply) = @@ -1034,7 +1034,7 @@ module HintParser = match operator with | "|" -> Pattern.Or(patLhs, patRhs) | "::" -> Pattern.Cons(patLhs, patRhs) - | _ -> failwith ("Unexpected operator " + operator + " in pattern.")) + | _ -> failwith ($"Unexpected operator '{operator}' in pattern.")) opp.AddOperator(op) do diff --git a/src/FSharpLint.Core/Framework/Utilities.fs b/src/FSharpLint.Core/Framework/Utilities.fs index 860dcc150..4ba2e6a5b 100644 --- a/src/FSharpLint.Core/Framework/Utilities.fs +++ b/src/FSharpLint.Core/Framework/Utilities.fs @@ -52,7 +52,7 @@ module ExpressionUtilities = PrettyNaming.ConvertValLogicalNameToDisplayNameCore ident.idText else ident.idText - let identAsCompiledOpName (identName: string) = + let identAsCompiledOpName (identName: string) = if PrettyNaming.IsOperatorDisplayName identName then PrettyNaming.CompileOpName identName else @@ -106,7 +106,7 @@ module ExpressionUtilities = let synTypeToString (text:string) = function | SynType.Tuple _ as synType -> tryFindTextOfRange synType.Range text - |> Option.map (fun x -> "(" + x + ")") + |> Option.map (fun x -> $"({x})") | other -> tryFindTextOfRange other.Range text diff --git a/src/FSharpLint.Core/Rules/Conventions/Binding/FavourAsKeyword.fs b/src/FSharpLint.Core/Rules/Conventions/Binding/FavourAsKeyword.fs index dc7333289..1d3d3cdbc 100644 --- a/src/FSharpLint.Core/Rules/Conventions/Binding/FavourAsKeyword.fs +++ b/src/FSharpLint.Core/Rules/Conventions/Binding/FavourAsKeyword.fs @@ -29,8 +29,8 @@ let private checkForNamedPatternEqualsConstant (args:AstNodeRuleParams) pattern ExpressionUtilities.tryFindTextOfRange constRange args.FileContent |> Option.bind (fun constText -> - - lazy (Some { FromText = text; FromRange = fromRange; ToText = constText + " as " + ident.idText}) + + lazy (Some { FromText = text; FromRange = fromRange; ToText = $"{constText} as {ident.idText}"}) |> Some ) diff --git a/src/FSharpLint.Core/Rules/Conventions/Binding/TupleOfWildcards.fs b/src/FSharpLint.Core/Rules/Conventions/Binding/TupleOfWildcards.fs index c3766b5d8..fbb394065 100644 --- a/src/FSharpLint.Core/Rules/Conventions/Binding/TupleOfWildcards.fs +++ b/src/FSharpLint.Core/Rules/Conventions/Binding/TupleOfWildcards.fs @@ -16,14 +16,14 @@ let private checkTupleOfWildcards pattern identifier = let constructorString numberOfWildcards = let constructorName = identifier |> String.concat "." let arguments = Array.create numberOfWildcards "_" |> String.concat ", " - constructorName + "(" + arguments + ")" + $"{constructorName}({arguments})" match pattern with | SynPat.Tuple(_isStruct, patterns, _, range) when List.length patterns > 1 && patterns |> List.forall isWildcard -> let errorFormat = Resources.GetString("RulesTupleOfWildcardsError") let refactorFrom = constructorString (List.length patterns) - let refactorTo = (constructorString 1) - let error = System.String.Format(errorFormat, refactorFrom, refactorTo) + let refactorTo = constructorString 1 + let error = String.Format(errorFormat, refactorFrom, refactorTo) { Range = range; Message = error; SuggestedFix = None; TypeChecks = [] } |> Array.singleton | _ -> Array.empty diff --git a/src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs b/src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs index 7971c2c06..d2738940a 100644 --- a/src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs +++ b/src/FSharpLint.Core/Rules/Conventions/Naming/NamingHelper.fs @@ -242,7 +242,7 @@ let getAccessControlLevel (syntaxArray:AbstractSyntaxArray.Node []) i = /// Is an attribute with a given name? /// e.g. check for Literal attribute. let isAttribute name (attributes:SynAttributes) = - let fullName = name + "Attribute" + let fullName = $"{name}Attribute" let attributeHasExpectedName (attribute:SynAttribute) = match List.tryLast attribute.TypeName.LongIdent with diff --git a/src/FSharpLint.Core/Rules/Conventions/RaiseWithTooManyArguments/FailwithBadUsage.fs b/src/FSharpLint.Core/Rules/Conventions/RaiseWithTooManyArguments/FailwithBadUsage.fs index 6737be32e..c2bab841a 100644 --- a/src/FSharpLint.Core/Rules/Conventions/RaiseWithTooManyArguments/FailwithBadUsage.fs +++ b/src/FSharpLint.Core/Rules/Conventions/RaiseWithTooManyArguments/FailwithBadUsage.fs @@ -37,9 +37,9 @@ let private runner (args: AstNodeRuleParams) = Some( lazy (Some - { FromText = sprintf "%s %s" failwithKeyword failwithErrorMessage + { FromText = $"%s{failwithKeyword} %s{failwithErrorMessage}" FromRange = range - ToText = sprintf "raise <| Exception(\"%s\", %s)" failwithErrorMessage param }) + ToText = $"raise <| Exception(\"%s{failwithErrorMessage}\", %s{param})" }) ) | _ -> None diff --git a/src/FSharpLint.Core/Rules/Conventions/SuggestUseAutoProperty.fs b/src/FSharpLint.Core/Rules/Conventions/SuggestUseAutoProperty.fs index 80d7a3c15..69f8716dd 100644 --- a/src/FSharpLint.Core/Rules/Conventions/SuggestUseAutoProperty.fs +++ b/src/FSharpLint.Core/Rules/Conventions/SuggestUseAutoProperty.fs @@ -80,7 +80,7 @@ let private runner (args: AstNodeRuleParams) = Some { FromText = args.FileContent FromRange = memberIdentifier.Range - ToText = "val " + memberName.idText } + ToText = $"val {memberName.idText}" } | _ -> None) { Range = memberRange diff --git a/src/FSharpLint.Core/Rules/Formatting/TupleFormatting/TupleParentheses.fs b/src/FSharpLint.Core/Rules/Formatting/TupleFormatting/TupleParentheses.fs index a49b4c12d..23d96ddaf 100644 --- a/src/FSharpLint.Core/Rules/Formatting/TupleFormatting/TupleParentheses.fs +++ b/src/FSharpLint.Core/Rules/Formatting/TupleFormatting/TupleParentheses.fs @@ -16,7 +16,7 @@ let checkTupleHasParentheses (args:AstNodeRuleParams) _ range parentNode = ExpressionUtilities.tryFindTextOfRange range args.FileContent |> Option.map (fun text -> let suggestedFix = lazy( - { FromRange = range; FromText = text; ToText = "(" + text + ")" } + { FromRange = range; FromText = text; ToText = $"({text})" } |> Some) { Range = range Message = Resources.GetString("RulesFormattingTupleParenthesesError") diff --git a/src/FSharpLint.Core/Rules/Formatting/TypePrefixing.fs b/src/FSharpLint.Core/Rules/Formatting/TypePrefixing.fs index f9b15e5f2..3cae5dda8 100644 --- a/src/FSharpLint.Core/Rules/Formatting/TypePrefixing.fs +++ b/src/FSharpLint.Core/Rules/Formatting/TypePrefixing.fs @@ -23,7 +23,7 @@ let checkTypePrefixing (config:Config) (args:AstNodeRuleParams) range typeName t let prefixSuggestion typeName = let suggestedFix = lazy( (ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs) - ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeName + "<" + typeArgs + ">" })) + ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = $"{typeName}<{typeArgs}>" })) { Range = range Message = Resources.GetString("RulesFormattingGenericPrefixError") SuggestedFix = Some suggestedFix @@ -42,7 +42,7 @@ let checkTypePrefixing (config:Config) (args:AstNodeRuleParams) range typeName t then let suggestedFix = lazy( (ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs) - ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeArgs + " " + typeName })) + ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = $"{typeArgs} {typeName}" })) { Range = range Message = String.Format(recommendPostfixErrMsg.Value, typeName) SuggestedFix = Some suggestedFix @@ -57,7 +57,7 @@ let checkTypePrefixing (config:Config) (args:AstNodeRuleParams) range typeName t // Prefer special postfix (e.g. int []). let suggestedFix = lazy( (ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs) - ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeArgs + " []" })) + ||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = $"{typeArgs} []" })) { Range = range Message = Resources.GetString("RulesFormattingF#ArrayPostfixError") SuggestedFix = Some suggestedFix @@ -90,7 +90,7 @@ let runner (config:Config) args = { Range = range Message = Resources.GetString("RulesFormattingF#ArrayPrefixError") SuggestedFix = None - TypeChecks = List.Empty } + TypeChecks = List.Empty } |> Array.singleton | _ -> Array.empty diff --git a/src/FSharpLint.Core/Rules/Formatting/TypedItemSpacing.fs b/src/FSharpLint.Core/Rules/Formatting/TypedItemSpacing.fs index 5da1b1c84..a28fd51f4 100644 --- a/src/FSharpLint.Core/Rules/Formatting/TypedItemSpacing.fs +++ b/src/FSharpLint.Core/Rules/Formatting/TypedItemSpacing.fs @@ -56,7 +56,7 @@ let private checkRange (config:Config) (args:AstNodeRuleParams) (range:Range) = let spacesBeforeString = " " |> String.replicate expectedSpacesBefore let spacesAfterString = " " |> String.replicate expectedSpacesAfter let suggestedFix = lazy( - { FromRange = range; FromText = text; ToText = trimmedOtherText + spacesBeforeString + ":" + spacesAfterString + trimmedTypeText } + { FromRange = range; FromText = text; ToText = $"{trimmedOtherText}{spacesBeforeString}:{spacesAfterString}{trimmedTypeText}" } |> Some) let errorFormatString = Resources.GetString("RulesFormattingTypedItemSpacingError") Some diff --git a/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs b/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs index eaf3d1ff2..0860507bc 100644 --- a/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs +++ b/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs @@ -460,23 +460,23 @@ module private MatchPattern = module private FormatHint = let private constantToString = function | Constant.Bool(x) -> if x then "true" else "false" - | Constant.Int16(x) -> x.ToString() + "s" - | Constant.Int32(x) -> x.ToString() - | Constant.Int64(x) -> x.ToString() + "L" - | Constant.UInt16(x) -> x.ToString() + "us" - | Constant.UInt32(x) -> x.ToString() + "u" - | Constant.UInt64(x) -> x.ToString() + "UL" - | Constant.Byte(x) -> x.ToString() + "uy" - | Constant.Bytes(x) -> x.ToString() - | Constant.Char(x) -> "'" + x.ToString() + "'" - | Constant.Decimal(x) -> x.ToString() + "m" - | Constant.Double(x) -> x.ToString() - | Constant.SByte(x) -> x.ToString() + "y" - | Constant.Single(x) -> x.ToString() + "f" - | Constant.String(x) -> "\"" + x + "\"" - | Constant.UIntPtr(x) -> x.ToString() - | Constant.IntPtr(x) -> x.ToString() - | Constant.UserNum(x, _) -> x.ToString() + | Constant.Int16(x) -> $"{x}s" + | Constant.Int32(x) -> $"{x}" + | Constant.Int64(x) -> $"{x}L" + | Constant.UInt16(x) -> $"{x}us" + | Constant.UInt32(x) -> $"{x}u" + | Constant.UInt64(x) -> $"{x}UL" + | Constant.Byte(x) -> $"{x}uy" + | Constant.Bytes(x) -> $"{x}" + | Constant.Char(x) -> $"'{x}'" + | Constant.Decimal(x) -> $"{x}m" + | Constant.Double(x) -> $"{x}" + | Constant.SByte(x) -> $"{x}y" + | Constant.Single(x) -> $"{x}f" + | Constant.String(x) -> $"\"{x}\"" + | Constant.UIntPtr(x) -> $"{x}" + | Constant.IntPtr(x) -> $"{x}" + | Constant.UserNum(x, _) -> $"{x}" | Constant.Unit -> "()" let private surroundExpressionsString hintToString left right sep expressions = @@ -490,7 +490,7 @@ module private FormatHint = let private opToString = function | Expression.Identifier(identifier) -> String.concat "." identifier | x -> - Debug.Assert(false, "Expected operator to be an expression identifier, but was " + x.ToString()) + Debug.Assert(false, $"Expected operator to be an expression identifier, but was {x.ToString()}") "" let rec toString replace parentAstNode (args:AstNodeRuleParams) (matchedVariables:Dictionary<_, SynExpr>) parentHintNode hintNode = @@ -517,28 +517,26 @@ module private FormatHint = identifier |> List.map (fun each -> if PrettyNaming.IsOperatorDisplayName each then - sprintf "( %s )" each + $"( %s{each} )" else each) |> String.concat "." | HintExpr(Expression.FunctionApplication(expressions)) -> expressions |> surroundExpressionsString (HintExpr >> toString) "" "" " " | HintExpr(Expression.InfixOperator(operator, leftHint, rightHint)) -> - toString (HintExpr leftHint) + " " + opToString operator + " " + toString (HintExpr rightHint) + $"{toString (HintExpr leftHint)} {opToString operator} {toString (HintExpr rightHint)}" | HintPat(Pattern.Cons(leftHint, rightHint)) -> - toString (HintPat leftHint) + "::" + toString (HintPat rightHint) + $"{toString (HintPat leftHint)}::{toString (HintPat rightHint)}" | HintPat(Pattern.Or(leftHint, rightHint)) -> - toString (HintPat leftHint) + " | " + toString (HintPat rightHint) + $"{toString (HintPat leftHint)} | {toString (HintPat rightHint)}" | HintExpr(Expression.AddressOf(singleAmp, hint)) -> (if singleAmp then "&" else "&&") + toString (HintExpr hint) | HintExpr(Expression.PrefixOperator(operator, hint)) -> - opToString operator + toString (HintExpr hint) - | HintExpr(Expression.Parentheses(hint)) -> "(" + toString (HintExpr hint) + ")" - | HintPat(Pattern.Parentheses(hint)) -> "(" + toString (HintPat hint) + ")" + $"{opToString operator}{toString (HintExpr hint)}" + | HintExpr(Expression.Parentheses(hint)) -> $"({toString (HintExpr hint)})" + | HintPat(Pattern.Parentheses(hint)) -> $"({toString (HintPat hint)})" | HintExpr(Expression.Lambda(arguments, LambdaBody(body))) -> - "fun " - + lambdaArgumentsToString replace parentAstNode args matchedVariables arguments - + " -> " + toString (HintExpr body) + $"fun {lambdaArgumentsToString replace parentAstNode args matchedVariables arguments} -> {toString (HintExpr body)}" | HintExpr(Expression.LambdaArg(argument)) -> toString (HintExpr argument) | HintExpr(Expression.LambdaBody(body)) -> @@ -556,14 +554,14 @@ module private FormatHint = | HintPat(Pattern.Array(expressions)) -> expressions |> surroundExpressionsString (HintPat >> toString) "[|" "|]" ";" | HintExpr(Expression.If(cond, expr, None)) -> - "if " + toString (HintExpr cond) + " then " + toString (HintExpr expr) + $"if {toString (HintExpr cond)} then {toString (HintExpr expr)}" | HintExpr(Expression.If(cond, expr, Some(elseExpr))) -> - "if " + toString (HintExpr cond) + " then " + toString (HintExpr expr) + " " + toString (HintExpr elseExpr) + $"if {toString (HintExpr cond)} then {toString (HintExpr expr)} {toString (HintExpr elseExpr)}" | HintExpr(Expression.Else(expr)) -> - "else " + toString (HintExpr expr) + $"else {toString (HintExpr expr)}" | HintExpr(Expression.Null) | HintPat(Pattern.Null) -> "null" - if replace && Precedence.requiresParenthesis matchedVariables hintNode parentAstNode parentHintNode then "(" + str + ")" + if replace && Precedence.requiresParenthesis matchedVariables hintNode parentAstNode parentHintNode then $"({str})" else str and private lambdaArgumentsToString replace parentAstNode args matchedVariables (arguments:LambdaArg list) = arguments diff --git a/tests/FSharpLint.Core.Tests/Framework/TestAst.fs b/tests/FSharpLint.Core.Tests/Framework/TestAst.fs index 0e3aa0006..8c33790cb 100644 --- a/tests/FSharpLint.Core.Tests/Framework/TestAst.fs +++ b/tests/FSharpLint.Core.Tests/Framework/TestAst.fs @@ -49,7 +49,7 @@ let stubPropertyInitialiser propertyName value = type TestAst() = [] - member __.GetSuppressMessageAttributesWithConstructorArgs() = + member _.GetSuppressMessageAttributesWithConstructorArgs() = let attributes = [ [stubConstString "Analyser"; stubConstString "Rule"] |> stubTuple @@ -63,7 +63,7 @@ type TestAst() = Assert.AreEqual({ category = "Analyser"; rule = "Rule" }, attrs |> List.head |> fst) [] - member __.GetSuppressMessageAttributesWithPropertyInitialisers() = + member _.GetSuppressMessageAttributesWithPropertyInitialisers() = let attributes = [ [stubPropertyInitialiser "Category" "Analyser"; stubPropertyInitialiser "CheckId" "*"] |> stubTuple @@ -77,7 +77,7 @@ type TestAst() = Assert.AreEqual({ category = "Analyser"; rule = "*" }, attrs |> List.head |> fst) [] - member __.GetSuppressMessageAttributesWithPropertyInitialisersMissingCategoryProperty() = + member _.GetSuppressMessageAttributesWithPropertyInitialisersMissingCategoryProperty() = let attributes = [ [stubPropertyInitialiser "SomeProp" "Analyser"; stubPropertyInitialiser "CheckId" "*"] |> stubTuple @@ -89,7 +89,7 @@ type TestAst() = Assert.IsEmpty(getSuppressMessageAttributes binding) [] - member __.GetSuppressMessageAttributesWithPropertyInitialisersMissingCheckIdProperty() = + member _.GetSuppressMessageAttributesWithPropertyInitialisersMissingCheckIdProperty() = let attributes = [ [stubPropertyInitialiser "Category" "Analyser"; stubPropertyInitialiser "SomeProp" "*"] |> stubTuple @@ -101,7 +101,7 @@ type TestAst() = Assert.IsEmpty(getSuppressMessageAttributes binding) [] - member __.GetSuppressMessageAttributesWithPropertyInitialisersWithExtraProperty() = + member _.GetSuppressMessageAttributesWithPropertyInitialisersWithExtraProperty() = let attributes = [ [ stubPropertyInitialiser "AnotherProp" "gwegweg" stubPropertyInitialiser "Category" "Analyser" diff --git a/tests/FSharpLint.Core.Tests/Rules/Conventions/CyclomaticComplexity.fs b/tests/FSharpLint.Core.Tests/Rules/Conventions/CyclomaticComplexity.fs index 7e00f6746..ba207271e 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Conventions/CyclomaticComplexity.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Conventions/CyclomaticComplexity.fs @@ -21,14 +21,16 @@ let private indent numSpaces (s: string) = /// Generates a body of code containing a match expression. let private makeMatchSnippet len = + let patterns = Seq.map (sprintf "| \"%d\" -> ()") [| 1..len-1 |] |> String.concat NewLine $"""match "dummyString" with -{Seq.map (fun i -> (sprintf "| \"%d\" -> ()" i)) [| 1..len-1 |] |> String.concat NewLine} +{patterns} | _ -> ()""" /// Generates a body of code containing a match expression with a when clause containing a logical operator in each pattern. let private makeMatchSnippetWithLogicalOperatorsInWhenClause len = + let patterns = Seq.map (fun i -> $"| x when x = \"%d{i*len}\" || x = \"%d{i*len+1}\" -> ()") [| 1..len-1 |] |> String.concat NewLine $"""match "dummyString" with -{Seq.map (fun i -> (sprintf "| x when x = \"%d\" || x = \"%d\" -> ()" (i*len) (i*len+1))) [| 1..len-1 |] |> String.concat NewLine} +{patterns} | _ -> ()""" /// module declaration and let binding declaration for a body of code @@ -40,40 +42,40 @@ let {funcString} = /// Generates a body of code consisting of if-else expressions. let private ifElseExpressions len = $"""if true then () - {String.replicate (len-1) (sprintf "%selif true then ()" NewLine)}""" + {String.replicate (len-1) $"%s{NewLine}elif true then ()"}""" |> makeProgram "f()" /// Generates a body of code containing for expressions. let private forExpressions len = - String.replicate len (sprintf "for i = 0 to 1 do ()%s" NewLine) + String.replicate len $"for i = 0 to 1 do ()%s{NewLine}" |> makeProgram "f()" /// Generates a body of code containing foreach expressions. let private foreachExpressions len = - String.replicate len (sprintf "for _ in [] do ()%s" NewLine) + String.replicate len $"for _ in [] do ()%s{NewLine}" |> makeProgram "f()" /// Generates a body of code containing while expressions. let private whileExpressions len = - String.replicate len (sprintf "while false do ()%s" NewLine) + String.replicate len $"while false do ()%s{NewLine}" |> makeProgram "f()" /// Generates a body of code containing a while expression with multiple logical operators in the condition. let private whileWithBooleanOperatorsInConditionExpressions len = if len < 2 then invalidArg (nameof len) "must be > 2" - $"""while true && {String.replicate (len-2) (sprintf "%sfalse &&" NewLine)} true do ()""" + $"""while true && {String.replicate (len-2) $"%s{NewLine}false &&"} true do ()""" |> makeProgram "f()" /// Generates a body of code containing an if statement with multiple && conditional operators let private ifThenExpressionWithMultipleAndConditionals len = if len < 2 then invalidArg (nameof len) "must be > 2" - $"""if true && {String.replicate (len-2) (sprintf "%sfalse &&" NewLine)} true then ()""" + $"""if true && {String.replicate (len-2) $"%s{NewLine}false &&"} true then ()""" |> makeProgram "f()" /// Generates a body of code containing an if statement with multiple || conditional operators let private ifThenExpressionWithMultipleOrConditionals len = if len < 2 then invalidArg (nameof len) "must be > 2" - $"""if true || {String.replicate (len-2) (sprintf "%sfalse ||" NewLine)} true then ()""" + $"""if true || {String.replicate (len-2) $"%s{NewLine}false ||"} true then ()""" |> makeProgram "f()" /// Generates a body of code containing a match expression with multiple patterns. @@ -83,24 +85,26 @@ let private matchExpression len = /// Generates a body of code containing a match expression with multiple combined patterns. let private matchExpressionWithCombinedPatterns len = + let patterns = Seq.map (sprintf "| \"%d\"") [| 1..len-1 |] |> String.concat NewLine $"""match "dummyString" with -{(Seq.map (fun i -> (sprintf "| \"%d\"" i)) [| 1..len-1 |] |> String.concat NewLine)} +{patterns} | _ -> ()""" |> makeProgram "f()" /// Generates a body of code containing a match function with multiple patterns. let private matchFunction len = $""" function -{(Seq.map (fun i -> (sprintf " | \"%d\"" i)) [| 1..len-1 |] |> String.concat NewLine)} +{(Seq.map (sprintf " | \"%d\"") [| 1..len-1 |] |> String.concat NewLine)} | _ -> () f "dummyString" """ |> makeProgram "f" /// Generates a computational expression with a match! expression containing multiple patterns. let private matchBang len = + let patterns = Seq.map (sprintf " | \"%d\"") [| 1..len-1 |] |> String.concat NewLine $"""async {{ match! async {{ return "dummyString" }} with -{(Seq.map (fun i -> (sprintf " | \"%d\"" i)) [| 1..len-1 |] |> String.concat NewLine)} +{patterns} | _ -> () }}""" |> makeProgram "a" diff --git a/tests/FSharpLint.Core.Tests/Rules/Conventions/SourceLength.fs b/tests/FSharpLint.Core.Tests/Rules/Conventions/SourceLength.fs index 4f6516e79..34d8669d5 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Conventions/SourceLength.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Conventions/SourceLength.fs @@ -14,12 +14,12 @@ let generateNewLines numNewLines numIndents = String.Empty else String.replicate numIndents " " - sprintf "%sprintf System.String.Empty\n" indentationChars) + $"{indentationChars}printf System.String.Empty\n") (Array.create numNewLines "") |> String.concat "" let generateAbstractMembers numMembers numIndents = - Array.init numMembers (fun index -> sprintf "abstract member Foo%i : unit -> unit\n" index) + Array.init numMembers (fun index -> $"abstract member Foo%i{index} : unit -> unit\n") |> String.concat (String.replicate numIndents " ") [] @@ -30,40 +30,40 @@ type TestMaxLinesInFunction() = [] member this.FunctionTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog x = - %s - ()""" (generateNewLines FunctionLength 4)) + %s{generateNewLines FunctionLength 4} + ()""") Assert.IsTrue(this.ErrorExistsAt(4, 4)) [] member this.FunctionNotTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog x = - %s - ()""" (generateNewLines (FunctionLength - 4) 4)) + %s{generateNewLines (FunctionLength - 4) 4} + ()""") Assert.IsFalse(this.ErrorExistsAt(4, 4)) [] member this.FunctionTooManyLinesWithComment() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog x = // Foo // Bar // Buzz - %s - ()""" (generateNewLines (FunctionLength - 3) 4)) + %s{generateNewLines (FunctionLength - 3) 4} + ()""") Assert.IsFalse this.ErrorsExist [] member this.FunctionTooManyLinesWithMultiLineComment() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog x = @@ -71,13 +71,13 @@ let dog x = Foo Bar *) - %s - ()""" (generateNewLines (FunctionLength - 4) 4)) + %s{generateNewLines (FunctionLength - 4) 4} + ()""") Assert.IsFalse this.ErrorsExist [] member this.FunctionTooManyLinesWithNestsedMultiLineComment() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog x = @@ -87,8 +87,8 @@ let dog x = Bar *) let (*) a b = a + b - %s - ()""" (generateNewLines (FunctionLength - 5) 4)) + %s{generateNewLines (FunctionLength - 5) 4} + ()""") Assert.IsFalse this.ErrorsExist [] @@ -99,30 +99,30 @@ type TestMaxLinesInLambdaFunction() = [] member this.LambdaFunctionTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = fun x -> match x with | Some(x) -> - %s + %s{generateNewLines LambdaFunctionLength 8} () | None -> () - """ (generateNewLines LambdaFunctionLength 8)) + """) Assert.IsTrue(this.ErrorExistsAt(4, 10)) [] member this.``Multiple arguments in a lamba should not be treated as separate lambdas.``() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = fun x y -> match x with | Some(x) -> - %s + %s{generateNewLines LambdaFunctionLength 8} () | None -> () - """ (generateNewLines LambdaFunctionLength 8)) + """) Assert.AreEqual(1, Seq.length <| this.ErrorsAt(4, 10)) @@ -148,26 +148,26 @@ type TestMaxLinesInMatchLambdaFunction() = [] member this.MatchFunctionTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = function | Some(x) -> - %s + %s{generateNewLines MatchLambdaFunctionLength 4} () -| None -> ()""" (generateNewLines MatchLambdaFunctionLength 4)) +| None -> ()""") Assert.IsTrue(this.ErrorExistsAt(4, 10)) [] member this.MatchFunctionNotTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = function | Some(x) -> - %s + %s{generateNewLines (MatchLambdaFunctionLength - 5) 4} () -| None -> ()""" (generateNewLines (MatchLambdaFunctionLength - 5) 4)) +| None -> ()""") Assert.IsFalse(this.ErrorExistsAt(4, 4)) [] @@ -178,22 +178,22 @@ type TestMaxLinesInValue() = [] member this.ValueTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = - %s - ()""" (generateNewLines ValueLength 4)) + %s{generateNewLines ValueLength 4} + ()""") Assert.IsTrue(this.ErrorExistsAt(4, 4)) [] member this.ValueNotTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program let dog = - %s - ()""" (generateNewLines (ValueLength - 4) 4)) + %s{generateNewLines (ValueLength - 4) 4} + ()""") Assert.IsFalse(this.ErrorExistsAt(4, 4)) [] @@ -204,14 +204,14 @@ type TestMaxLinesInConstructor() = [] member this.ConstructorTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program type MyClass(x) = new() = - %s + %s{generateNewLines ConstructorLength 8} MyClass(0) - """ (generateNewLines ConstructorLength 8)) + """) Assert.IsTrue(this.ErrorExistsAt(5, 4)) [] @@ -258,13 +258,13 @@ type TestMaxLinesInClass() = [] member this.ClassTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program type MyClass2() as this = do - %s - member this.PrintMessage() = ()""" (generateNewLines ClassLength 8)) + %s{generateNewLines ClassLength 8} + member this.PrintMessage() = ()""") Assert.IsTrue(this.ErrorExistsAt(4, 5)) [] @@ -280,12 +280,12 @@ type MyClass2() as this = [] member this.InterfaceTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program type IPrintable = - %s - abstract member Print : unit -> unit""" (generateAbstractMembers ClassLength 4)) + %s{generateAbstractMembers ClassLength 4} + abstract member Print : unit -> unit""") Assert.IsTrue(this.ErrorExistsAt(4, 5)) @@ -315,14 +315,14 @@ type TestMaxLinesInRecord() = [] member this.RecordTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program type Record = - { - %s + {{ + %s{generateNewLines RecordLength 8} dog: int - }""" (generateNewLines RecordLength 8)) + }}""") Assert.IsTrue(this.ErrorExistsAt(4, 5)) [] @@ -350,18 +350,18 @@ type TestMaxLinesInModule() = [] member this.ModuleTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program -%s +{generateNewLines ModuleLength 0} let foo = "" -exception SomeException of string""" (generateNewLines ModuleLength 0)) +exception SomeException of string""") Assert.IsTrue(this.ErrorExistsAt(2, 0)) [] member this.ModuleNotTooManyLines() = - this.Parse(sprintf """ + this.Parse($""" module Program -%s +{generateNewLines (ModuleLength - 4) 0} let foo = "" -exception SomeException of string""" (generateNewLines (ModuleLength - 4) 0)) +exception SomeException of string""") Assert.IsFalse(this.ErrorExistsAt(2, 0)) diff --git a/tests/FSharpLint.Core.Tests/Rules/TestRuleBase.fs b/tests/FSharpLint.Core.Tests/Rules/TestRuleBase.fs index 7df58e6b3..169886a5e 100644 --- a/tests/FSharpLint.Core.Tests/Rules/TestRuleBase.fs +++ b/tests/FSharpLint.Core.Tests/Rules/TestRuleBase.fs @@ -58,9 +58,7 @@ type TestRuleBase () = | xs when xs.Count = 0 -> "No errors" | _ -> suggestions - |> Seq.map (fun s -> (sprintf "((%i, %i) - (%i, %i) -> %s)" - s.Details.Range.StartRange.StartLine s.Details.Range.StartColumn - s.Details.Range.EndRange.EndLine s.Details.Range.EndRange.EndColumn s.Details.Message )) + |> Seq.map (fun s -> $"(({s.Details.Range.StartRange.StartLine}, {s.Details.Range.StartColumn}) - ({s.Details.Range.EndRange.EndLine}, {s.Details.Range.EndRange.EndColumn}) -> {s.Details.Message})") |> (fun x -> String.Join("; ", x)) member this.ErrorWithMessageExistsAt(message, startLine, startColumn) = @@ -69,7 +67,8 @@ type TestRuleBase () = member _.AssertErrorWithMessageExists(message) = let foundSuggestions = suggestions |> Seq.map (fun s -> s.Details.Message) - Assert.IsTrue(foundSuggestions |> Seq.contains message, sprintf "Couldn't find message '%s', found: [%s]" message (String.concat "," foundSuggestions)) + let foundSuggestionsStr = String.concat "," foundSuggestions + Assert.IsTrue(foundSuggestions |> Seq.contains message, $"Couldn't find message '{message}', found: [{foundSuggestionsStr}]") member this.AssertNoWarnings() = Assert.IsFalse(this.ErrorsExist, "Expected no errors, but was: " + this.ErrorMsg) diff --git a/tests/FSharpLint.Core.Tests/Rules/Typography/NoTabCharacters.fs b/tests/FSharpLint.Core.Tests/Rules/Typography/NoTabCharacters.fs index be435465e..39513d541 100644 --- a/tests/FSharpLint.Core.Tests/Rules/Typography/NoTabCharacters.fs +++ b/tests/FSharpLint.Core.Tests/Rules/Typography/NoTabCharacters.fs @@ -1,5 +1,6 @@ module FSharpLint.Core.Tests.Rules.Typography.NoTabCharacters +open System open NUnit.Framework open FSharpLint.Rules open FSharpLint.Core.Tests @@ -16,12 +17,13 @@ type TestTypographyTabCharacterInFile() = [] member this.``Tab character in literal strings are not reported``() = - this.Parse (sprintf """ - let a = @"a%sb" - let b = %s - a%sb - %s - """ "\t" "\"\"\"" "\t" "\"\"\"") + let source = String.Format(""" + let a = @"a{0}b" + let b = {1} + a{0}b + {1} + """, "\t", "\"\"\"") + this.Parse (source) Assert.IsFalse(this.ErrorExistsAt(2, 23)) Assert.IsFalse(this.ErrorExistsAt(4, 13)) diff --git a/tests/FSharpLint.FunctionalTest/TestConsoleApplication.fs b/tests/FSharpLint.FunctionalTest/TestConsoleApplication.fs index 13836e72c..f85e283ee 100644 --- a/tests/FSharpLint.FunctionalTest/TestConsoleApplication.fs +++ b/tests/FSharpLint.FunctionalTest/TestConsoleApplication.fs @@ -20,7 +20,7 @@ module Tests = Code:string } override this.ToString() = - sprintf "{\n Description=\"%s\"\n Location=\"%s\"\n Code=\"%s\"\n}" this.Description this.Location this.Code + $"{{%s{Environment.NewLine} Description=\"%s{this.Description}\"%s{Environment.NewLine} Location=\"%s{this.Location}\"%s{Environment.NewLine} Code=\"%s{this.Code}\"%s{Environment.NewLine}}}" let dotnetFslint arguments = let configDirName = @@ -81,7 +81,7 @@ module Tests = member _.InvalidConfig() = let projectFile = projectPath "FSharpLint.FunctionalTest.TestedProject.NetCore.fsproj" let lintConfigPath = projectPath "fsharplint.json" - let arguments = sprintf "lint --lint-config %s %s" lintConfigPath projectFile + let arguments = $"lint --lint-config %s{lintConfigPath} %s{projectFile}" File.WriteAllText(lintConfigPath, "invalid config file contents") @@ -89,47 +89,51 @@ module Tests = File.Delete(projectPath "fsharplint.json") - Assert.IsTrue(output.Contains("Failed while reading from config at run time"), sprintf "Output:\n%s" output) + Assert.IsTrue(output.Contains("Failed while reading from config at run time"), $"Output:%s{Environment.NewLine}%s{output}") [] member _.UnableToFindProjectFile() = let projectFile = projectPath "iuniubi.fsproj" - let arguments = sprintf "lint %s" projectFile + let arguments = $"lint %s{projectFile}" let output = dotnetFslint arguments Assert.IsTrue( - output.Contains(sprintf "Could not find the file: %s on disk" projectFile), - sprintf "Output:\n%s" output) + output.Contains($"Could not find the file: %s{projectFile} on disk"), + $"Output:%s{Environment.NewLine}%s{output}") [] member _.FunctionalTestConsoleApplication() = let projectFile = projectPath "FSharpLint.FunctionalTest.TestedProject.NetCore.fsproj" - let arguments = sprintf "lint %s" projectFile + let arguments = $"lint {projectFile}" let output = dotnetFslint arguments let errors = getErrorsFromOutput output let expectedMissing = Set.difference expectedErrors errors let notExpected = Set.difference errors expectedErrors + let expectedMissingStr = String.concat "," expectedMissing + let notExpectedStr = String.concat "," notExpected Assert.AreEqual(expectedErrors, errors, - "Did not find the following expected errors: [" + String.concat "," expectedMissing + "]\n" + - "Found the following unexpected warnings: [" + String.concat "," notExpected + "]\n" + - "Complete output: " + output) + $"Did not find the following expected errors: [{expectedMissingStr}]\n" + + $"Found the following unexpected warnings: [{notExpectedStr}]\n" + + $"Complete output: {output}") [] member _.FunctionalTestConsoleApplicationSolution() = let solutionFile = basePath "tests" "FSharpLint.FunctionalTest.TestedProject" "FSharpLint.FunctionalTest.TestedProject.sln" - let arguments = sprintf "lint %s" solutionFile + let arguments = $"lint {solutionFile}" let output = dotnetFslint arguments let errors = getErrorsFromOutput output let expectedMissing = Set.difference expectedErrors errors let notExpected = Set.difference errors expectedErrors + let expectedMissingStr = String.concat "," expectedMissing + let notExpectedStr = String.concat "," notExpected Assert.AreEqual(expectedErrors, errors, - "Did not find the following expected errors: [" + String.concat "," expectedMissing + "]\n" + - "Found the following unexpected warnings: [" + String.concat "," notExpected + "]\n" + - "Complete output: " + output) + $"Did not find the following expected errors: [{expectedMissingStr}]\n" + + $"Found the following unexpected warnings: [{notExpectedStr}]\n" + + $"Complete output: {output}")