Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/performance/008.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ let b = [] = a
let b = List.isEmpty a // will check if there is no tail which is faster than the equality check.
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-008
let b = [] = a
```

## Code fix

This analyzer has a code fix for Ionide:
Expand Down
12 changes: 12 additions & 0 deletions docs/performance/009.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ let (|Int|_|) str =
| _ -> ValueNone
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-009
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| true, int -> Some(int)
| _ -> None
```

## Code fix

This analyzer has a code fix for Ionide:
Expand Down
12 changes: 12 additions & 0 deletions docs/performance/010.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ let a b =
b
|> List.choose (fun x -> if x < 20 then Some(x * 2) else None)
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-010
let a b =
b
|> List.filter (fun x -> x < 20)
|> List.map (fun y -> y * 2)
```
9 changes: 9 additions & 0 deletions docs/performance/011.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ a = null
isNull a
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-011
a = null
```

## Code fix

This analyzer has a code fix for Ionide:
Expand Down
9 changes: 9 additions & 0 deletions docs/style/002.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ let c: string seq = Seq.empty
let d: string option = None
let e: string voption = ValueNone
let f: string ref = ref ""
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-002
let a: string[] = Array.empty
```
11 changes: 10 additions & 1 deletion docs/suggestion/001.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ let moved = { X = 1; Y = 2 }

If you use the Ionide integration, there's a quick fix available:

![RecordUpdateAnalyzerFix](../img/RecordUpdateAnalyzerFix.gif)
![RecordUpdateAnalyzerFix](../img/RecordUpdateAnalyzerFix.gif)

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-001
let moved = { zero with X = 1; Y = 2 }
```
11 changes: 11 additions & 0 deletions docs/suggestion/003.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,14 @@ Ensure the function is being executed:
```fsharp
ignore (a())
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
let f g () = g + 1
let a = f 1
// IGNORE: IONIDE-002
ignore a
```
10 changes: 10 additions & 0 deletions docs/suggestion/004.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ Add the missing name:
type DU =
| Example of age: int * name: string
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-003
type DU =
| Example of int * name: string
```
10 changes: 10 additions & 0 deletions docs/suggestion/005.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ Use the `Length` property if you know the reference is not null or the `String.I
let s = "foo"
let b = s.Length = 0
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
let s = "foo"
// IGNORE: IONIDE-004
let b = s.Length = 0
```
10 changes: 10 additions & 0 deletions docs/suggestion/006.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,13 @@ match option with
// or
Option.defaultValue () option
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
let option = None
// IGNORE: IONIDE-005
let value = Option.get option
```
11 changes: 11 additions & 0 deletions docs/suggestion/007.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ match [] with
| _ -> ()
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
match [] with
// IGNORE: IONIDE-006
| x :: [] -> ()
| _ -> ()
```


## Code fix

Expand Down
13 changes: 12 additions & 1 deletion docs/suggestion/012.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,20 @@ type DU =
| Three of b:float
```

## Ignore Comment

You can ignore this analyzer on a line by adding the following comment:

```fsharp
// IGNORE: IONIDE-007
type DU =
| One
| Two of a:int
| Three of b:float
```

## Code fix

This analyzer has a code fix for Ionide:


![code fix for EqualsNullAnalyzer](../img/StructDiscriminatedUnionAnalyzer.gif)
28 changes: 28 additions & 0 deletions src/Ionide.Analyzers/Ignore.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Ionide.Analyzers

open System
open FSharp.Compiler.SyntaxTrivia
open FSharp.Compiler.Text

[<RequireQualifiedAccess>]
module Ignore =

/// checks there is an ignore comment on the line above the range
let hasComment
(magicComment: string)
(comments: CommentTrivia list)
(sourceText: ISourceText)
(analyzerTriggeredOn: Range)
: CommentTrivia option
=
comments
|> List.tryFind (fun c ->
match c with
| CommentTrivia.BlockComment r
| CommentTrivia.LineComment r ->
if r.StartLine <> analyzerTriggeredOn.StartLine - 1 then
false
else
let lineOfComment = sourceText.GetLineString(r.StartLine - 1) // 0-based
lineOfComment.Contains(magicComment, StringComparison.OrdinalIgnoreCase)
)
10 changes: 10 additions & 0 deletions src/Ionide.Analyzers/InputOperations.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Ionide.Analyzers

open FSharp.Compiler.Syntax

module InputOperations =

let getCodeComments input =
match input with
| ParsedInput.ImplFile parsedFileInput -> parsedFileInput.Trivia.CodeComments
| ParsedInput.SigFile parsedSigFileInput -> parsedSigFileInput.Trivia.CodeComments
2 changes: 2 additions & 0 deletions src/Ionide.Analyzers/Ionide.Analyzers.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<Compile Include="LanguageVersionShim.fs" />
<Compile Include="UntypedOperations.fs" />
<Compile Include="TypedOperations.fs" />
<Compile Include="InputOperations.fs" />
<Compile Include="Ignore.fs" />
<Compile Include="Suggestion\CopyAndUpdateRecordChangesAllFieldsAnalyzer.fs" />
<Compile Include="Suggestion\IgnoreFunctionAnalyzer.fs" />
<Compile Include="Suggestion\UnnamedDiscriminatedUnionFieldAnalyzer.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
module Ionide.Analyzers.Performance.CombinePipedModuleFunctionsAnalyzer

open Ionide.Analyzers
open System.Collections.Generic
open FSharp.Compiler.Text
open FSharp.Compiler.Syntax
open FSharp.Analyzers.SDK
open FSharp.Analyzers.SDK.ASTCollecting
open Ionide.Analyzers.UntypedOperations

[<Literal>]
let ignoreComment = "IGNORE: IONIDE-010"

[<return: Struct>]
let (|PipeInfixApp|_|) synExpr =
match synExpr with
Expand Down Expand Up @@ -54,8 +58,10 @@ type MessageData =
Range: Range
}

let private analyze (parsedInput: ParsedInput) : Message list =
let private analyze (sourceText: ISourceText) (parsedInput: ParsedInput) : Message list =
let xs = HashSet<MessageData>()
let comments = InputOperations.getCodeComments parsedInput
let hasIgnoreComment = Ignore.hasComment ignoreComment comments sourceText

let collector =
{ new SyntaxCollectorBase() with
Expand All @@ -68,14 +74,17 @@ let private analyze (parsedInput: ParsedInput) : Message list =
| ModuleFunction(m1, f1) :: ModuleFunction(m2, f2) :: rest when m1.idText = m2.idText ->
let m = Range.unionRanges m1.idRange m2.idRange

xs.Add
{
ModuleName = m1.idText
Function1 = f1.idText
Function2 = f2.idText
Range = m
}
|> ignore
match hasIgnoreComment m with
| Some _ -> ()
| None ->
xs.Add
{
ModuleName = m1.idText
Function1 = f1.idText
Function2 = f2.idText
Range = m
}
|> ignore

visit rest
| _ :: rest -> visit rest
Expand Down Expand Up @@ -116,8 +125,8 @@ let helpUri = "https://ionide.io/ionide-analyzers/performance/010.html"

[<CliAnalyzer(name, shortDescription, helpUri)>]
let combinePipedModuleFunctionsCliAnalyzer: Analyzer<CliContext> =
fun (context: CliContext) -> async { return analyze context.ParseFileResults.ParseTree }
fun (context: CliContext) -> async { return analyze context.SourceText context.ParseFileResults.ParseTree }

[<EditorAnalyzer(name, shortDescription, helpUri)>]
let combinePipedModuleFunctionsEditorAnalyzer: Analyzer<EditorContext> =
fun (context: EditorContext) -> async { return analyze context.ParseFileResults.ParseTree }
fun (context: EditorContext) -> async { return analyze context.SourceText context.ParseFileResults.ParseTree }
16 changes: 14 additions & 2 deletions src/Ionide.Analyzers/Performance/EqualsNullAnalyzer.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Ionide.Analyzers.Performance.EqualsNullAnalyzer

open Ionide.Analyzers
open System.Collections.Generic
open FSharp.Compiler.Text
open FSharp.Compiler.Syntax
Expand All @@ -9,6 +10,9 @@ open Ionide.Analyzers.UntypedOperations
open Ionide.Analyzers.TypedOperations
open FSharp.Compiler.CodeAnalysis

[<Literal>]
let ignoreComment = "IGNORE: IONIDE-011"

[<Literal>]
let equalsMessage = "`a = null` is suboptimal, use `isNull a` instead."

Expand All @@ -33,18 +37,26 @@ let private analyze
: Message list
=
let xs = HashSet<EqualsNullOperation>()
let comments = InputOperations.getCodeComments parsedInput

let hasIgnoreComment =
Ignore.hasComment ignoreComment comments sourceText >> Option.isSome

let collector =
{ new SyntaxCollectorBase() with
override x.WalkExpr(path, synExpr) =
match synExpr with
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(opIdent, argExpr), SynExpr.Null _, m)
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(opIdent, SynExpr.Null _), argExpr, m) ->
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(opIdent, SynExpr.Null _), argExpr, m) when
not <| hasIgnoreComment m
->
xs.Add(EqualsNullOperation(false, argExpr.Range, opIdent, addParens argExpr, m))
|> ignore

| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpInequality(opIdent, argExpr), SynExpr.Null _, m)
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpInequality(opIdent, SynExpr.Null _), argExpr, m) ->
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpInequality(opIdent, SynExpr.Null _), argExpr, m) when
not <| hasIgnoreComment m
->
xs.Add(EqualsNullOperation(true, argExpr.Range, opIdent, addParens argExpr, m))
|> ignore
| _ -> ()
Expand Down
12 changes: 11 additions & 1 deletion src/Ionide.Analyzers/Performance/ListEqualsEmptyListAnalyzer.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Ionide.Analyzers.Performance.ListEqualsEmptyListAnalyzer

open Ionide.Analyzers
open System.Collections.Generic
open FSharp.Compiler.Text
open FSharp.Compiler.Syntax
Expand All @@ -9,6 +10,9 @@ open FSharp.Analyzers.SDK.ASTCollecting
open Ionide.Analyzers.UntypedOperations
open Ionide.Analyzers.TypedOperations

[<Literal>]
let ignoreComment = "IGNORE: IONIDE-008"

[<Literal>]
let message = "list = [] is suboptimal, use List.isEmpty"

Expand All @@ -28,13 +32,19 @@ let private analyze
: Message list
=
let xs = HashSet<EqualsOperation>()
let comments = InputOperations.getCodeComments parsedInput

let hasIgnoreComment =
Ignore.hasComment ignoreComment comments sourceText >> Option.isSome

let collector =
{ new SyntaxCollectorBase() with
override x.WalkExpr(path, synExpr) =
match synExpr with
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(operatorIdent, argExpr), EmptyList, m)
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(operatorIdent, EmptyList), argExpr, m) ->
| SynExpr.App(ExprAtomicFlag.NonAtomic, false, OpEquality(operatorIdent, EmptyList), argExpr, m) when
not <| hasIgnoreComment m
->
xs.Add(EqualsOperation(operatorIdent, argExpr.Range, m)) |> ignore
| _ -> ()
}
Expand Down
Loading