diff --git a/.gitattributes b/.gitattributes index 2b2d87f..863b8f0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,4 +4,5 @@ # Always use lf for F# files *.fs text eol=lf *.fsx text eol=lf -*.fsi text eol=lf \ No newline at end of file +*.fsi text eol=lf +*.expected text eol=lf \ No newline at end of file diff --git a/src/FSharp.Analyzers/Codes.fs b/src/FSharp.Analyzers/Codes.fs index dc54e46..4623313 100644 --- a/src/FSharp.Analyzers/Codes.fs +++ b/src/FSharp.Analyzers/Codes.fs @@ -5,3 +5,6 @@ let StringEndsWith = "GRA-001" [] let StringStartsWith = "GRA-002" + +[] +let StringIndexOf = "GRA-003" diff --git a/src/FSharp.Analyzers/StringAnalyzers.fs b/src/FSharp.Analyzers/StringAnalyzers.fs index 5570e42..03a554f 100644 --- a/src/FSharp.Analyzers/StringAnalyzers.fs +++ b/src/FSharp.Analyzers/StringAnalyzers.fs @@ -140,9 +140,10 @@ module StringAnalyzers = else fsharpType.GenericArguments |> Seq.map visit |> String.concat " -> " - visit mfv.FullType = expectedSignature + let actualSignature = visit mfv.FullType + actualSignature = expectedSignature - [] + [] let endsWithAnalyzer (ctx : CliContext) : Async = invalidStringFunctionUseAnalyzer "EndsWith" @@ -157,7 +158,7 @@ module StringAnalyzers = | _ -> false) (hasMatchingSignature "System.String -> System.Boolean") - [] + [] let startsWithAnalyzer (ctx : CliContext) : Async = invalidStringFunctionUseAnalyzer "StartsWith" @@ -171,3 +172,18 @@ module StringAnalyzers = | SingleStringArgumentExpr _ -> true | _ -> false) (hasMatchingSignature "System.String -> System.Boolean") + + [] + let indexOfAnalyzer (ctx : CliContext) : Async = + invalidStringFunctionUseAnalyzer + "IndexOf" + Codes.StringIndexOf + "The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload." + Warning + ctx.SourceText + ctx.ParseFileResults.ParseTree + ctx.CheckFileResults + (function + | SingleStringArgumentExpr _ -> true + | _ -> false) + (hasMatchingSignature "System.String -> System.Int32") diff --git a/src/FSharp.Analyzers/StringAnalyzers.fsi b/src/FSharp.Analyzers/StringAnalyzers.fsi index 49bd96d..69cdf62 100644 --- a/src/FSharp.Analyzers/StringAnalyzers.fsi +++ b/src/FSharp.Analyzers/StringAnalyzers.fsi @@ -3,8 +3,11 @@ namespace ``G-Research``.FSharp.Analyzers open FSharp.Analyzers.SDK module StringAnalyzers = - [] + [] val endsWithAnalyzer : ctx : CliContext -> Async - [] + [] val startsWithAnalyzer : ctx : CliContext -> Async + + [] + val indexOfAnalyzer : ctx : CliContext -> Async 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..4a4c32c --- /dev/null +++ b/tests/data/string/indexof/Constant String - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-003 | Warning | (1,6 - 1,13) | 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..c659fc3 --- /dev/null +++ b/tests/data/string/indexof/Function call - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-003 | Warning | (2,4 - 2,11) | 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..8425d45 --- /dev/null +++ b/tests/data/string/indexof/Prefixed Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-003 | Warning | (4,4 - 4,11) | 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..affccf7 --- /dev/null +++ b/tests/data/string/indexof/Value - Single Argument.fs.expected @@ -0,0 +1 @@ +GRA-003 | Warning | (2,2 - 2,9) | The usage of String.IndexOf with a single string argument is discouraged. Signal your intention explicitly by calling an overload.