Skip to content

Commit dbf2753

Browse files
committed
Console,Core: fix FSharpLint warning
Fixing the warning for NoPartialFunctions rule. Also to use FSharpx.Collections.Seq.tryHeadTail, we must install and add FSharpx.Collections to our dependencies: ``` Consider using 'FSharpx.Collections.Seq.tryHeadTail' instead of partial function/method 'Array.tail'. Error on line 126 starting at column 19 |> Array.tail ```
1 parent 9116f2b commit dbf2753

File tree

12 files changed

+81
-37
lines changed

12 files changed

+81
-37
lines changed

paket.dependencies

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ nuget Argu
77
nuget BenchmarkDotNet
88
nuget BenchmarkDotNet.Diagnostics.Windows
99
nuget FParsec
10+
nuget FSharpx.Collections
1011
nuget FSharp.Compiler.Service ~> 41.0.1
1112
nuget Ionide.ProjInfo.ProjectSystem == 0.61.3
1213
nuget Ionide.ProjInfo.FCS == 0.61.3
@@ -60,4 +61,4 @@ group Build
6061
nuget Fake.Core.UserInput
6162
nuget Fake.IO.FileSystem
6263
nuget Fake.DotNet.MsBuild
63-
nuget Fake.Api.GitHub
64+
nuget Fake.Api.GitHub

paket.lock

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ NUGET
5959
FSharp.Core (>= 4.7.2)
6060
System.Reactive (>= 5.0 < 6.0)
6161
FSharp.Core (6.0.7)
62+
FSharpx.Collections (3.1)
63+
FSharp.Core (>= 4.3.4)
6264
Gee.External.Capstone (2.3)
6365
Iced (1.20)
6466
Ionide.ProjInfo (0.61.3)

src/FSharpLint.Console/Output.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type StandardOutput () =
2424
else
2525
errorLine
2626
|> Seq.mapi (fun index _ -> if index = range.StartColumn then "^" else " ")
27-
|> Seq.reduce (+)
27+
|> Seq.fold (+) String.Empty
2828
getErrorMessage range + Environment.NewLine + errorLine + Environment.NewLine + highlightColumnLine
2929

3030
let writeLine (str:string) (color:ConsoleColor) (writer:IO.TextWriter) =

src/FSharpLint.Console/Program.fs

+4-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ let private start (arguments:ParseResults<ToolArgs>) (toolsPath:Ionide.ProjInfo.
8686
if arguments.Contains ToolArgs.Version then
8787
let version =
8888
Assembly.GetExecutingAssembly().GetCustomAttributes false
89-
|> Seq.pick (function | :? AssemblyInformationalVersionAttribute as aiva -> Some aiva.InformationalVersion | _ -> None)
90-
sprintf "Current version: %s" version |> output.WriteInfo
89+
|> Seq.tryPick (function | :? AssemblyInformationalVersionAttribute as aiva -> Some aiva.InformationalVersion | _ -> None)
90+
match version with
91+
| Some ver -> sprintf "Current version: %s" ver |> output.WriteInfo
92+
| None -> output.WriteInfo "No version information found in assembly attributes."
9193
()
9294

9395
let handleError (str:string) =

src/FSharpLint.Core/Application/Configuration.fs

+10-2
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,16 @@ let loadConfig (configPath:string) =
597597
/// This function loads and returns this default configuration.
598598
let defaultConfiguration =
599599
let assembly = typeof<Rules.Rule>.GetTypeInfo().Assembly
600-
let resourceName = Assembly.GetExecutingAssembly().GetManifestResourceNames()
601-
|> Seq.find (fun resourceFile -> resourceFile.EndsWith("fsharplint.json", System.StringComparison.Ordinal))
600+
let resourceName =
601+
let fsharplintJson =
602+
Assembly.GetExecutingAssembly().GetManifestResourceNames()
603+
|> Seq.tryFind (fun resourceFile ->
604+
resourceFile.EndsWith("fsharplint.json", System.StringComparison.Ordinal))
605+
606+
match fsharplintJson with
607+
| Some value -> value
608+
| None -> failwith "Resource file 'fsharplint.json' not found in the assembly."
609+
602610
use stream = assembly.GetManifestResourceStream(resourceName)
603611
match stream with
604612
| null -> failwithf "Resource '%s' not found in assembly '%s'" resourceName (assembly.FullName)

src/FSharpLint.Core/Framework/HintParser.fs

+7-5
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,9 @@ module HintParser =
272272

273273
let private getIdentifierHashCode = function
274274
| identifier when (List.isEmpty >> not) identifier ->
275-
identifier
276-
|> Seq.last
277-
|> ExpressionUtilities.identAsCompiledOpName
278-
|> hash
275+
match (Seq.tryLast identifier) with
276+
| Some value -> value |> ExpressionUtilities.identAsCompiledOpName |> hash
277+
| None -> failwith "There's no last element in identifier."
279278
| _ -> 0
280279

281280
let rec private getHashCode node =
@@ -497,7 +496,10 @@ module HintParser =
497496
let private pescapechar: Parser<char, unit> =
498497
skipChar '\\'
499498
>>. pischar ['"';'\\';'\'';'n';'t';'b';'r';'a';'f';'v']
500-
|>> fun escapeChar -> Map.find escapeChar escapeMap
499+
|>> fun escapeChar ->
500+
match (Map.tryFind escapeChar escapeMap) with
501+
| Some value -> value
502+
| None -> failwith "Invalid escape character."
501503

502504
let private pnonescapechars: Parser<char, unit> =
503505
skipChar '\\'

src/FSharpLint.Core/Framework/Resources.fs

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ open System.Resources
66
/// Provides a way of getting string values from the framework's resource files (files in src/FSharpLint.Framework/Resources/).
77
/// Used to retrieve multi-lingual strings inside of the app.
88
type Resources() =
9-
static let resourceName = Assembly.GetExecutingAssembly().GetManifestResourceNames()
10-
|> Seq.find (fun resource -> resource.EndsWith("Text.resources", System.StringComparison.Ordinal))
9+
static let resourceName =
10+
let name =
11+
Assembly.GetExecutingAssembly().GetManifestResourceNames()
12+
|> Seq.tryFind (fun resource -> resource.EndsWith("Text.resources", System.StringComparison.Ordinal))
13+
14+
match name with
15+
| Some value -> value
16+
| None -> failwith "There were no resources found."
1117

1218
static let resourceManager = ResourceManager(resourceName.Replace(".resources", System.String.Empty), typeof<Resources>.GetTypeInfo().Assembly)
1319

src/FSharpLint.Core/Framework/Utilities.fs

+12-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module ExpressionUtilities =
2929
open FSharp.Compiler.Text
3030
open FSharp.Compiler.CodeAnalysis
3131

32+
open FSharpx.Collections
33+
3234
let (|Identifier|_|) = function
3335
| SynExpr.Ident(ident) -> Some([ident], ident.idRange)
3436
| SynExpr.LongIdent(_, longIdent, _, _) -> Some(longIdent.LongIdent, longIdent.Range)
@@ -121,17 +123,19 @@ module ExpressionUtilities =
121123
let countPrecedingCommentLines (text:string) (startPos:pos) (endPos:pos) =
122124
let range = Range.mkRange String.Empty startPos endPos
123125

124-
let map (precedingText: string) =
126+
let processComments (text:string) =
125127
let lines =
126-
precedingText.Split '\n'
127-
|> Array.rev
128-
|> Array.tail
129-
lines
130-
|> Array.takeWhile (fun line -> line.TrimStart().StartsWith("//"))
131-
|> Array.length
128+
text.Split '\n'
129+
|> Seq.rev
130+
match Seq.tryHeadTail lines with
131+
| Some (_, tail) ->
132+
tail
133+
|> Seq.takeWhile (fun line -> line.TrimStart().StartsWith("//"))
134+
|> Seq.length
135+
| None -> 0
132136

133137
tryFindTextOfRange range text
134-
|> Option.map map
138+
|> Option.map processComments
135139
|> Option.defaultValue 0
136140

137141
let rangeContainsOtherRange (containingRange:Range) (range:Range) =

src/FSharpLint.Core/Rules/Conventions/CyclomaticComplexity.fs

+19-8
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,31 @@ type private BindingStack(maxComplexity: int) =
4646
let isChildOfCurrent = if List.isEmpty tier1 then
4747
false
4848
else
49-
args.GetParents args.NodeIndex |> List.tryFind (fun astNode -> Object.ReferenceEquals(tier1.Head.Node, astNode)) |> Option.isSome
49+
args.GetParents args.NodeIndex
50+
|> List.tryFind (fun astNode ->
51+
match (List.tryHead tier1) with
52+
| Some head -> Object.ReferenceEquals(head.Node, astNode)
53+
| None -> false)
54+
|> Option.isSome
5055
// if the node is not a child and the stack isn't empty, we're finished with the current head of tier1, so move it from tier1 to tier2
5156
if not isChildOfCurrent && not (List.isEmpty tier1) then
52-
let popped = tier1.Head
53-
tier1 <- tier1.Tail
54-
if popped.Complexity > maxComplexity then
55-
tier2.Add popped |> ignore<bool>
57+
let popped = List.tryHead tier1
58+
match popped with
59+
| Some value ->
60+
tier1 <- tier1.Tail
61+
if value.Complexity > maxComplexity then
62+
tier2.Add value |> ignore<bool>
63+
| None -> ()
5664
// finally, push the item on to the stack
5765
tier1 <- bs::tier1
5866

5967
member this.IncrComplexityOfCurrentScope incr =
60-
let head = tier1.Head
61-
let complexity = head.Complexity + incr
62-
tier1 <- {head with Complexity = complexity}::tier1.Tail
68+
let head = List.tryHead tier1
69+
match head with
70+
| Some value ->
71+
let complexity = value.Complexity + incr
72+
tier1 <- {value with Complexity = complexity}::tier1.Tail
73+
| None -> ()
6374

6475
interface IEnumerable<BindingScope> with
6576
member this.GetEnumerator() =

src/FSharpLint.Core/Rules/Conventions/FunctionReimplementation/CanBeReplacedWithComposition.fs

+11-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ open FSharpLint.Framework.ExpressionUtilities
1010

1111
let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
1212
let canBeReplacedWithFunctionComposition expression =
13-
let getLastElement = List.rev >> List.head
13+
let getLastElement = List.rev >> List.tryHead
1414

1515
let rec lambdaArgumentIsLastApplicationInFunctionCalls expression (lambdaArgument:Ident) numFunctionCalls =
1616
let rec appliedValuesAreConstants appliedValues =
@@ -25,12 +25,16 @@ let private validateLambdaCannotBeReplacedWithComposition _ lambda range =
2525
| (SynExpr.Ident(_) | SynExpr.LongIdent(_))::appliedValues
2626
when appliedValuesAreConstants appliedValues ->
2727

28-
match getLastElement appliedValues with
29-
| SynExpr.Ident(lastArgument) when numFunctionCalls > 1 ->
30-
lastArgument.idText = lambdaArgument.idText
31-
| SynExpr.App(_, false, _, _, _) as nextFunction ->
32-
lambdaArgumentIsLastApplicationInFunctionCalls nextFunction lambdaArgument (numFunctionCalls + 1)
33-
| _ -> false
28+
let lastElement = getLastElement appliedValues
29+
match lastElement with
30+
| Some element ->
31+
match element with
32+
| SynExpr.Ident(lastArgument) when numFunctionCalls > 1 ->
33+
lastArgument.idText = lambdaArgument.idText
34+
| SynExpr.App(_, false, _, _, _) as nextFunction ->
35+
lambdaArgumentIsLastApplicationInFunctionCalls nextFunction lambdaArgument (numFunctionCalls + 1)
36+
| _ -> false
37+
| None -> failwith "There's no last element."
3438
| _ -> false
3539
| _ -> false
3640

src/FSharpLint.Core/Rules/Conventions/NoPartialFunctions.fs

+4-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,10 @@ let private isNonStaticInstanceMemberCall (checkFile:FSharpCheckFileResults) nam
263263
if not (fullyQualifiedInstanceMember.Contains ".") then
264264
failwith "Please use fully qualified name for the instance member"
265265
let nameSegments = fullyQualifiedInstanceMember.Split '.'
266-
let instanceMemberNameOnly = Array.last nameSegments
266+
let instanceMemberNameOnly =
267+
match Array.tryLast nameSegments with
268+
| Some value -> value
269+
| None -> failwith "Instance member name cannot be parsed."
267270
let isSourcePropSameAsReplacementProp = List.tryFind (fun sourceInstanceMemberName -> sourceInstanceMemberName = instanceMemberNameOnly) names
268271
match isSourcePropSameAsReplacementProp with
269272
| Some _ ->

src/FSharpLint.Core/paket.references

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ Microsoft.Build.Framework
1111
Microsoft.Build.Locator
1212
Microsoft.Build.Utilities.Core
1313
Microsoft.Build
14+
FSharpx.Collections

0 commit comments

Comments
 (0)