diff --git a/src/FSharp.Analyzers/FSharp.Analyzers.fsproj b/src/FSharp.Analyzers/FSharp.Analyzers.fsproj index ae47b9c..3987cf8 100644 --- a/src/FSharp.Analyzers/FSharp.Analyzers.fsproj +++ b/src/FSharp.Analyzers/FSharp.Analyzers.fsproj @@ -1,4 +1,4 @@ - + net6.0 @@ -11,9 +11,10 @@ - + + diff --git a/src/FSharp.Analyzers/StringAnalyzer.fs b/src/FSharp.Analyzers/StringAnalyzer.fs new file mode 100644 index 0000000..81e18dc --- /dev/null +++ b/src/FSharp.Analyzers/StringAnalyzer.fs @@ -0,0 +1,127 @@ +module GR.FSharp.Analyzers.StringAnalyzer + +open FSharp.Analyzers.SDK +open FSharp.Compiler.Symbols +open FSharp.Compiler.Text +open GR.FSharp.Analyzers.TASTCollecting + +[] +let StringEndsWithCode = "GRA-STRING-001" + +[] +let StringStartsWithCode = "GRA-STRING-002" + +[] +let StringIndexOfCode = "GRA-STRING-003" + +let (|StringExpr|_|) (e : FSharpExpr) = + if e.Type.ErasedType.BasicQualifiedName = "System.String" then + Some () + else + None + +let (|IntExpr|_|) (e : FSharpExpr) = + if e.Type.ErasedType.BasicQualifiedName = "System.Int32" then + Some () + else + None + +let invalidStringFunctionUseAnalyzer + functionName + code + message + severity + (typedTree : FSharpImplementationFileContents) + (typedArgumentPredicate : FSharpExpr list -> bool) + = + let invocations = ResizeArray () + + let handler : Handler = + Handler.CallHandler (fun (m : range) (mfv : FSharpMemberOrFunctionOrValue) (args : FSharpExpr list) -> + if + mfv.Assembly.SimpleName = "System.Runtime" + && mfv.FullName = $"System.String.{functionName}" + && typedArgumentPredicate args + then + invocations.Add m + ) + + for decl in typedTree.Declarations do + visitDeclaration handler decl + + invocations + |> Seq.map (fun mFunctionName -> + { + Type = $"String.{functionName} analyzer" + Message = message + Code = code + Severity = severity + Range = mFunctionName + Fixes = [] + } + ) + |> Seq.toList + +[] +let endsWithAnalyzer (ctx : CliContext) : Async = + async { + match ctx.TypedTree with + | None -> return List.empty + | Some typedTree -> + + return + invalidStringFunctionUseAnalyzer + "EndsWith" + StringEndsWithCode + "The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload." + Warning + typedTree + (fun (args : FSharpExpr list) -> + match args with + | [ StringExpr ] -> true + | _ -> false + ) + } + +[] +let startsWithAnalyzer (ctx : CliContext) : Async = + async { + match ctx.TypedTree with + | None -> return List.empty + | Some typedTree -> + return + invalidStringFunctionUseAnalyzer + "StartsWith" + StringStartsWithCode + "The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload." + Warning + typedTree + (fun (args : FSharpExpr list) -> + match args with + | [ StringExpr ] -> true + | _ -> false + ) + } + +[] +let indexOfAnalyzer (ctx : CliContext) : Async = + async { + match ctx.TypedTree with + | None -> return List.empty + | Some typedTree -> + + return + invalidStringFunctionUseAnalyzer + "IndexOf" + StringIndexOfCode + "The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload." + Warning + typedTree + (fun args -> + match args with + | [ StringExpr ] + | [ StringExpr ; IntExpr ] + | [ StringExpr ; IntExpr ; IntExpr ] -> true + | _ -> false + ) + } diff --git a/src/FSharp.Analyzers/StringAnalyzer.fsi b/src/FSharp.Analyzers/StringAnalyzer.fsi new file mode 100644 index 0000000..716eb2f --- /dev/null +++ b/src/FSharp.Analyzers/StringAnalyzer.fsi @@ -0,0 +1,21 @@ +module GR.FSharp.Analyzers.StringAnalyzer + +open FSharp.Analyzers.SDK + +[] +val StringEndsWithCode : string = "GRA-STRING-001" + +[] +val StringStartsWithCode : string = "GRA-STRING-002" + +[] +val StringIndexOfCode : string = "GRA-STRING-003" + +[] +val endsWithAnalyzer : ctx : CliContext -> Async + +[] +val startsWithAnalyzer : ctx : CliContext -> Async + +[] +val indexOfAnalyzer : ctx : CliContext -> Async diff --git a/src/FSharp.Analyzers/StringAnalyzers.fs b/src/FSharp.Analyzers/StringAnalyzers.fs deleted file mode 100644 index 5df7004..0000000 --- a/src/FSharp.Analyzers/StringAnalyzers.fs +++ /dev/null @@ -1,106 +0,0 @@ -namespace GR.FSharp.Analyzers - -open FSharp.Analyzers.SDK -open FSharp.Compiler.Symbols -open FSharp.Compiler.Syntax -open FSharp.Compiler.Text - -module StringAnalyzers = - [] - let EndsWithCode = "GRA-001" - - let (|SingleNameInSynLongIdent|_|) name (lid : SynLongIdent) = - match lid with - | SynLongIdent (id = [ ident ]) when ident.idText = name -> Some ident.idRange - | _ -> None - - let (|SynLongIdentEndsWith|_|) name (lid : SynLongIdent) = - List.tryLast lid.LongIdent - |> Option.bind (fun ident -> if ident.idText = name then Some ident.idRange else None) - - let rec (|SingleArgumentExpr|_|) = - function - | SynExpr.Paren (expr = SingleArgumentExpr) - // "" - | SynExpr.Const (constant = SynConst.String _) - // a.b - | SynExpr.LongIdent _ - // a - | SynExpr.Ident _ -> Some () - | _ -> None - - let findAllInvocations (functionName : string) (ast : ParsedInput) : range list = - let collector = ResizeArray () - - let walker = - { new SyntaxCollectorBase() with - override _.WalkExpr (expr : SynExpr) = - match expr with - // "".EndsWith("a") - | SynExpr.App ( - isInfix = false - funcExpr = SynExpr.DotGet (longDotId = SingleNameInSynLongIdent functionName mFunctionName) - argExpr = SingleArgumentExpr) - - // w.EndsWith("a") - | SynExpr.App ( - funcExpr = SynExpr.LongIdent (longDotId = SynLongIdentEndsWith functionName mFunctionName) - argExpr = SingleArgumentExpr) -> collector.Add mFunctionName - - | _ -> () - } - - walkAst walker ast - - collector |> Seq.toList - - [] - let endsWithAnalyzer (ctx : CliContext) : Async = - async { - let invocations = findAllInvocations "EndsWith" ctx.ParseFileResults.ParseTree - - return - invocations - |> List.choose (fun mEndsWith -> - let lineText = ctx.SourceText.GetLineString (mEndsWith.EndLine - 1) - - let symbolUseOpt = - ctx.CheckFileResults.GetSymbolUseAtLocation ( - mEndsWith.EndLine, - mEndsWith.EndColumn, - lineText, - [ "EndsWith" ] - ) - - symbolUseOpt - |> Option.bind (fun symbolUse -> - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as mfv -> - if mfv.Assembly.SimpleName <> "netstandard" then - None - elif mfv.CurriedParameterGroups.Count <> 1 then - None - elif mfv.CurriedParameterGroups.[0].Count <> 1 then - None - elif - mfv.CurriedParameterGroups.[0].[0].Type.ErasedType.BasicQualifiedName - <> "System.String" - then - None - else - Some mEndsWith - | _ -> None - ) - ) - |> List.map (fun mEndsWith -> - { - Type = "String.EndsWith analyzer" - Message = - "The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload." - Code = EndsWithCode - Severity = Warning - Range = mEndsWith - Fixes = [] - } - ) - } diff --git a/tests/FSharp.Analyzers.Tests/Testing.fs b/tests/FSharp.Analyzers.Tests/Common.fs similarity index 92% rename from tests/FSharp.Analyzers.Tests/Testing.fs rename to tests/FSharp.Analyzers.Tests/Common.fs index 036227a..e469f18 100644 --- a/tests/FSharp.Analyzers.Tests/Testing.fs +++ b/tests/FSharp.Analyzers.Tests/Common.fs @@ -2,6 +2,7 @@ open System open System.IO +open System.Collections open System.Threading.Tasks open NUnit.Framework open FSharp.Analyzers.SDK @@ -46,12 +47,16 @@ let assertExpected sourceFile messages = let dataFolder = Path.Combine (__SOURCE_DIRECTORY__, "..", "data") -let constructTestCaseEnumerator (subDataPath : string array) = - let testsDirectory = Path.Combine (dataFolder, Path.Combine subDataPath) - - Directory.EnumerateFiles (testsDirectory, "*.fs") +let constructTestCaseEnumeratorAux (files : string seq) : IEnumerator = + files |> Seq.map (fun f -> let fileName = Path.GetRelativePath (dataFolder, f) [| fileName :> obj |] ) |> fun s -> s.GetEnumerator () + +let constructTestCaseEnumerator (subDataPath : string array) = + let testsDirectory = Path.Combine (dataFolder, Path.Combine subDataPath) + + Directory.EnumerateFiles (testsDirectory, "*.fs") + |> constructTestCaseEnumeratorAux diff --git a/tests/FSharp.Analyzers.Tests/FSharp.Analyzers.Tests.fsproj b/tests/FSharp.Analyzers.Tests/FSharp.Analyzers.Tests.fsproj index ce53dbd..c2d2272 100644 --- a/tests/FSharp.Analyzers.Tests/FSharp.Analyzers.Tests.fsproj +++ b/tests/FSharp.Analyzers.Tests/FSharp.Analyzers.Tests.fsproj @@ -10,10 +10,10 @@ - - + + diff --git a/tests/FSharp.Analyzers.Tests/StringAnalyzerTests.fs b/tests/FSharp.Analyzers.Tests/StringAnalyzerTests.fs index 4618d07..517440a 100644 --- a/tests/FSharp.Analyzers.Tests/StringAnalyzerTests.fs +++ b/tests/FSharp.Analyzers.Tests/StringAnalyzerTests.fs @@ -1,58 +1,76 @@ -namespace GR.FSharp.Analyzers.Tests +module GR.FSharp.Analyzers.Tests.StringAnalyzerTests -module StringAnalyzerTests = +open System.Collections +open System.IO +open FSharp.Compiler.CodeAnalysis +open NUnit.Framework +open FSharp.Analyzers.SDK.Testing +open GR.FSharp.Analyzers +open GR.FSharp.Analyzers.Tests.Common - open System.Collections - open System.IO - open FSharp.Compiler.CodeAnalysis - open NUnit.Framework - open FSharp.Analyzers.SDK.Testing - open GR.FSharp.Analyzers - open GR.FSharp.Analyzers.Tests.Common +let mutable projectOptions : FSharpProjectOptions = FSharpProjectOptions.zero - let mutable projectOptions : FSharpProjectOptions = FSharpProjectOptions.zero +[] +let Setup () = + task { + let! options = mkOptionsFromProject "net7.0" [] + projectOptions <- options + } - [] - let Setup () = - task { - let! options = mkOptionsFromProject "net6.0" [] - projectOptions <- options - } +type TestCases() = - type TestCases() = + interface IEnumerable with + member _.GetEnumerator () : IEnumerator = + [| "endswith" ; "indexof" ; "startswith" |] + |> Seq.collect (fun subFolder -> + let folder = Path.Combine (dataFolder, "string", subFolder) + Directory.EnumerateFiles (folder, "*.fs") + ) + |> constructTestCaseEnumeratorAux - interface IEnumerable with - member _.GetEnumerator () : IEnumerator = - constructTestCaseEnumerator [| "string" ; "endswith" |] +let findStringAnalyzerFor (fileName : string) = + fileName.Split Path.DirectorySeparatorChar + |> Array.skip 1 + |> Array.head + |> function + | "endswith" -> StringAnalyzer.endsWithAnalyzer + | "startswith" -> StringAnalyzer.startsWithAnalyzer + | "indexof" -> StringAnalyzer.indexOfAnalyzer + | unknown -> failwithf $"Unknown subfolder \"%s{unknown}\", please configure analyzer" - [)>] - let EndsWithTests (fileName : string) = - task { - let fileName = Path.Combine (dataFolder, fileName) +[)>] +let StringTests (fileName : string) = + task { + let fullPath = Path.Combine (dataFolder, fileName) - let! messages = - File.ReadAllText fileName - |> getContext projectOptions - |> StringAnalyzers.endsWithAnalyzer + let! messages = + File.ReadAllText fullPath + |> getContext projectOptions + |> findStringAnalyzerFor fileName - do! assertExpected fileName messages - } + do! assertExpected fullPath messages + } - type NegativeTestCases() = +type NegativeTestCases() = - interface IEnumerable with - member _.GetEnumerator () : IEnumerator = - constructTestCaseEnumerator [| "string" ; "endswith" ; "negative" |] + interface IEnumerable with + member _.GetEnumerator () : IEnumerator = + [| "endswith" ; "indexof" ; "startswith" |] + |> Seq.collect (fun subFolder -> + let folder = Path.Combine (dataFolder, "string", subFolder, "negative") + Directory.EnumerateFiles (folder, "*.fs") + ) + |> constructTestCaseEnumeratorAux - [)>] - let NegativeEndsWithTests (fileName : string) = - task { - let fileName = Path.Combine (dataFolder, fileName) +[)>] +let NegativeStringTests (fileName : string) = + task { + let fullPath = Path.Combine (dataFolder, fileName) - let! messages = - File.ReadAllText fileName - |> getContext projectOptions - |> StringAnalyzers.endsWithAnalyzer + let! messages = + File.ReadAllText fullPath + |> getContext projectOptions + |> findStringAnalyzerFor fileName - Assert.IsEmpty messages - } + Assert.IsEmpty messages + } diff --git a/tests/data/string/endswith/Constant String - Single Argument.fs.expected b/tests/data/string/endswith/Constant String - Single Argument.fs.expected index 248da2c..6cff563 100644 --- a/tests/data/string/endswith/Constant String - Single Argument.fs.expected +++ b/tests/data/string/endswith/Constant String - Single Argument.fs.expected @@ -1 +1 @@ -GRA-001 | Warning | (1,6 - 1,14) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. +GRA-STRING-001 | Warning | (1,0 - 1,19) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/endswith/Function call - Single Argument.fs b/tests/data/string/endswith/Function call - Single Argument.fs new file mode 100644 index 0000000..bd779b5 --- /dev/null +++ b/tests/data/string/endswith/Function call - Single Argument.fs @@ -0,0 +1,2 @@ +let f () = "f" +"g".EndsWith(f()) diff --git a/tests/data/string/endswith/Function call - Single Argument.fs.expected b/tests/data/string/endswith/Function call - Single Argument.fs.expected new file mode 100644 index 0000000..b47af2c --- /dev/null +++ b/tests/data/string/endswith/Function call - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-001 | Warning | (2,0 - 2,17) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/endswith/Prefixed Value - Single Argument.fs.expected b/tests/data/string/endswith/Prefixed Value - Single Argument.fs.expected index 9fe373d..f9319ac 100644 --- a/tests/data/string/endswith/Prefixed Value - Single Argument.fs.expected +++ b/tests/data/string/endswith/Prefixed Value - Single Argument.fs.expected @@ -1 +1 @@ -GRA-001 | Warning | (4,4 - 4,12) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. +GRA-STRING-001 | Warning | (4,0 - 4,17) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/endswith/Value - Single Argument.fs.expected b/tests/data/string/endswith/Value - Single Argument.fs.expected index bf94a73..1b9c66e 100644 --- a/tests/data/string/endswith/Value - Single Argument.fs.expected +++ b/tests/data/string/endswith/Value - Single Argument.fs.expected @@ -1 +1 @@ -GRA-001 | Warning | (2,2 - 2,10) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. +GRA-STRING-001 | Warning | (2,0 - 2,15) | The usage of String.EndsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Constant String - Single Argument.fs b/tests/data/string/indexof/Constant String - Single Argument.fs new file mode 100644 index 0000000..61ab7fa --- /dev/null +++ b/tests/data/string/indexof/Constant String - Single Argument.fs @@ -0,0 +1 @@ +"foo".IndexOf("p") diff --git a/tests/data/string/indexof/Constant String - Single Argument.fs.expected b/tests/data/string/indexof/Constant String - Single Argument.fs.expected new file mode 100644 index 0000000..478f1fd --- /dev/null +++ b/tests/data/string/indexof/Constant String - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (1,0 - 1,18) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Constant String - String Int Int.fs b/tests/data/string/indexof/Constant String - String Int Int.fs new file mode 100644 index 0000000..abaed65 --- /dev/null +++ b/tests/data/string/indexof/Constant String - String Int Int.fs @@ -0,0 +1 @@ +"foo".IndexOf("p", 0 ,1) diff --git a/tests/data/string/indexof/Constant String - String Int Int.fs.expected b/tests/data/string/indexof/Constant String - String Int Int.fs.expected new file mode 100644 index 0000000..57ee22c --- /dev/null +++ b/tests/data/string/indexof/Constant String - String Int Int.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (1,0 - 1,24) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Constant String - String Int.fs b/tests/data/string/indexof/Constant String - String Int.fs new file mode 100644 index 0000000..7b2e608 --- /dev/null +++ b/tests/data/string/indexof/Constant String - String Int.fs @@ -0,0 +1 @@ +"foo".IndexOf("p", 0) diff --git a/tests/data/string/indexof/Constant String - String Int.fs.expected b/tests/data/string/indexof/Constant String - String Int.fs.expected new file mode 100644 index 0000000..e42f730 --- /dev/null +++ b/tests/data/string/indexof/Constant String - String Int.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (1,0 - 1,21) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Function call - Single Argument.fs b/tests/data/string/indexof/Function call - Single Argument.fs new file mode 100644 index 0000000..d9b6e16 --- /dev/null +++ b/tests/data/string/indexof/Function call - Single Argument.fs @@ -0,0 +1,2 @@ +let f () = "f" +"g".IndexOf(f()) diff --git a/tests/data/string/indexof/Function call - Single Argument.fs.expected b/tests/data/string/indexof/Function call - Single Argument.fs.expected new file mode 100644 index 0000000..e191e8a --- /dev/null +++ b/tests/data/string/indexof/Function call - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (2,0 - 2,16) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Prefixed Value - Single Argument.fs b/tests/data/string/indexof/Prefixed Value - Single Argument.fs new file mode 100644 index 0000000..63de87d --- /dev/null +++ b/tests/data/string/indexof/Prefixed Value - Single Argument.fs @@ -0,0 +1,4 @@ +module A = + let b = "b" + +A.b.IndexOf("b") diff --git a/tests/data/string/indexof/Prefixed Value - Single Argument.fs.expected b/tests/data/string/indexof/Prefixed Value - Single Argument.fs.expected new file mode 100644 index 0000000..ac7723d --- /dev/null +++ b/tests/data/string/indexof/Prefixed Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (4,0 - 4,16) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/Value - Single Argument.fs b/tests/data/string/indexof/Value - Single Argument.fs new file mode 100644 index 0000000..deb7b20 --- /dev/null +++ b/tests/data/string/indexof/Value - Single Argument.fs @@ -0,0 +1,2 @@ +let a = "a" +a.IndexOf("a") diff --git a/tests/data/string/indexof/Value - Single Argument.fs.expected b/tests/data/string/indexof/Value - Single Argument.fs.expected new file mode 100644 index 0000000..9830255 --- /dev/null +++ b/tests/data/string/indexof/Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-003 | Warning | (2,0 - 2,14) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/indexof/negative/Char Int32 Int32.fs b/tests/data/string/indexof/negative/Char Int32 Int32.fs new file mode 100644 index 0000000..1286125 --- /dev/null +++ b/tests/data/string/indexof/negative/Char Int32 Int32.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf('f',0,1) diff --git a/tests/data/string/indexof/negative/Char Int32.fs b/tests/data/string/indexof/negative/Char Int32.fs new file mode 100644 index 0000000..ed7b119 --- /dev/null +++ b/tests/data/string/indexof/negative/Char Int32.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf('f',0) diff --git a/tests/data/string/indexof/negative/Char StringComparison.fs b/tests/data/string/indexof/negative/Char StringComparison.fs new file mode 100644 index 0000000..dcb1b05 --- /dev/null +++ b/tests/data/string/indexof/negative/Char StringComparison.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf('f',StringComparison.InvariantCulture) diff --git a/tests/data/string/indexof/negative/Char.fs b/tests/data/string/indexof/negative/Char.fs new file mode 100644 index 0000000..c89e3d9 --- /dev/null +++ b/tests/data/string/indexof/negative/Char.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf('f') diff --git a/tests/data/string/indexof/negative/String Int32 Int32 StringComparison.fs b/tests/data/string/indexof/negative/String Int32 Int32 StringComparison.fs new file mode 100644 index 0000000..480862e --- /dev/null +++ b/tests/data/string/indexof/negative/String Int32 Int32 StringComparison.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf("f",0,1,StringComparison.InvariantCulture) diff --git a/tests/data/string/indexof/negative/String Int32 StringComparison.fs b/tests/data/string/indexof/negative/String Int32 StringComparison.fs new file mode 100644 index 0000000..5d89ce8 --- /dev/null +++ b/tests/data/string/indexof/negative/String Int32 StringComparison.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf("f",0,StringComparison.InvariantCulture) diff --git a/tests/data/string/indexof/negative/String StringComparison.fs b/tests/data/string/indexof/negative/String StringComparison.fs new file mode 100644 index 0000000..b1a1f0f --- /dev/null +++ b/tests/data/string/indexof/negative/String StringComparison.fs @@ -0,0 +1,2 @@ +open System +"foo".IndexOf("f",StringComparison.InvariantCulture) diff --git a/tests/data/string/startswith/Constant String - Single Argument.fs b/tests/data/string/startswith/Constant String - Single Argument.fs new file mode 100644 index 0000000..a9176fa --- /dev/null +++ b/tests/data/string/startswith/Constant String - Single Argument.fs @@ -0,0 +1 @@ +"foo".StartsWith("p") diff --git a/tests/data/string/startswith/Constant String - Single Argument.fs.expected b/tests/data/string/startswith/Constant String - Single Argument.fs.expected new file mode 100644 index 0000000..5f20b9c --- /dev/null +++ b/tests/data/string/startswith/Constant String - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-002 | Warning | (1,0 - 1,21) | The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/startswith/Function call - Single Argument.fs b/tests/data/string/startswith/Function call - Single Argument.fs new file mode 100644 index 0000000..3f28681 --- /dev/null +++ b/tests/data/string/startswith/Function call - Single Argument.fs @@ -0,0 +1,2 @@ +let f () = "f" +"g".StartsWith(f()) diff --git a/tests/data/string/startswith/Function call - Single Argument.fs.expected b/tests/data/string/startswith/Function call - Single Argument.fs.expected new file mode 100644 index 0000000..2e5d90e --- /dev/null +++ b/tests/data/string/startswith/Function call - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-002 | Warning | (2,0 - 2,19) | The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/startswith/Prefixed Value - Single Argument.fs b/tests/data/string/startswith/Prefixed Value - Single Argument.fs new file mode 100644 index 0000000..3ea636e --- /dev/null +++ b/tests/data/string/startswith/Prefixed Value - Single Argument.fs @@ -0,0 +1,4 @@ +module A = + let b = "b" + +A.b.StartsWith("b") diff --git a/tests/data/string/startswith/Prefixed Value - Single Argument.fs.expected b/tests/data/string/startswith/Prefixed Value - Single Argument.fs.expected new file mode 100644 index 0000000..f52b1f5 --- /dev/null +++ b/tests/data/string/startswith/Prefixed Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-002 | Warning | (4,0 - 4,19) | The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/startswith/Value - Single Argument.fs b/tests/data/string/startswith/Value - Single Argument.fs new file mode 100644 index 0000000..18940c3 --- /dev/null +++ b/tests/data/string/startswith/Value - Single Argument.fs @@ -0,0 +1,2 @@ +let a = "a" +a.StartsWith("a") diff --git a/tests/data/string/startswith/Value - Single Argument.fs.expected b/tests/data/string/startswith/Value - Single Argument.fs.expected new file mode 100644 index 0000000..b9da5ce --- /dev/null +++ b/tests/data/string/startswith/Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-STRING-002 | Warning | (2,0 - 2,17) | The usage of String.StartsWith with a single string argument is discouraged. Signal your intention explicitly by calling an overload. diff --git a/tests/data/string/startswith/negative/Boolean CultureInfo.fs b/tests/data/string/startswith/negative/Boolean CultureInfo.fs new file mode 100644 index 0000000..a671a2e --- /dev/null +++ b/tests/data/string/startswith/negative/Boolean CultureInfo.fs @@ -0,0 +1,3 @@ +open System.Globalization + +"a".StartsWith("b", false, CultureInfo.InvariantCulture) diff --git a/tests/data/string/startswith/negative/Char.fs b/tests/data/string/startswith/negative/Char.fs new file mode 100644 index 0000000..2d1ef7d --- /dev/null +++ b/tests/data/string/startswith/negative/Char.fs @@ -0,0 +1 @@ +"a".StartsWith('b') diff --git a/tests/data/string/startswith/negative/StringComparison.fs b/tests/data/string/startswith/negative/StringComparison.fs new file mode 100644 index 0000000..a1df206 --- /dev/null +++ b/tests/data/string/startswith/negative/StringComparison.fs @@ -0,0 +1 @@ +"a".StartsWith("b", System.StringComparison.InvariantCulture)