Skip to content
Draft
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
1 change: 1 addition & 0 deletions src/FSharpLint.Core/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ module FSharpLint.Core.AssemblyInfo
open System.Runtime.CompilerServices

[<assembly: InternalsVisibleToAttribute("FSharpLint.Benchmarks")>]
[<assembly: InternalsVisibleToAttribute("FSharpLint.Core.Tests")>]

()
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module FSharpLint.Rules.Helper.SourceLength

open System
open System.Text
open System.Text.RegularExpressions
open FSharpLint.Framework
open FSharpLint.Framework.Suggestion
Expand Down Expand Up @@ -34,31 +35,38 @@ let rec private getTopLevelBalancedPairs (toProcess: List<MultilineCommentMarker
| [ beginIndex ] -> (beginIndex, index) :: getTopLevelBalancedPairs tail List.Empty
| _::restOfStack -> getTopLevelBalancedPairs tail restOfStack

let internal stripMultilineComments (source: string) =
let markers =
multilineCommentMarkerRegex.Matches source
|> Seq.map (fun markerMatch ->
let index = markerMatch.Index
if source.[index] = '(' then
Begin index
else
End index)
|> Seq.sortBy (function | Begin index -> index | End index -> index)
|> Seq.toList

// Process block comment removal
// - If no comments, return input as is
// - If one comment, just remove it directly
// - If several comments, remove them all starting from the last, as removing them from the front changes the offsets of later ones
match getTopLevelBalancedPairs markers List.Empty with
| [] -> source
| [ (startIndex, endIndex) ] -> source.Remove(startIndex, (endIndex + multilineCommentMarkerRegexCaptureGroupLength) - startIndex )
| pairs ->

(pairs, StringBuilder(source))
||> List.foldBack
(fun (startIndex, endIndex)(currSource: StringBuilder) ->
currSource.Remove(startIndex, (endIndex + multilineCommentMarkerRegexCaptureGroupLength) - startIndex))
|> _.ToString()

let checkSourceLengthRule (config:Config) range fileContents errorName (skipRanges: array<Range>) =
let error name lineCount actual =
let errorFormatString = Resources.GetString("RulesSourceLengthError")
String.Format(errorFormatString, name, lineCount, actual)

let stripMultilineComments (source: string) =
let markers =
multilineCommentMarkerRegex.Matches source
|> Seq.map (fun markerMatch ->
let index = markerMatch.Index
if source.[index] = '(' then
Begin index
else
End index)
|> Seq.sortBy (function | Begin index -> index | End index -> index)
|> Seq.toList

getTopLevelBalancedPairs markers List.Empty
|> List.fold
(fun (currSource: string) (startIndex, endIndex) ->
let left = currSource.AsSpan(0, startIndex)
let right = currSource.AsSpan(endIndex + multilineCommentMarkerRegexCaptureGroupLength)
String.Concat(left, right))
source

match tryFindTextOfRange range fileContents with
| Some(sourceCode) ->
let sourceCode =
Expand Down
104 changes: 103 additions & 1 deletion tests/FSharpLint.Core.Tests/Rules/Conventions/SourceLength.fs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ let dog x =
()""")
Assert.IsFalse this.ErrorsExist

[<Test>]
member this.FunctionTooManyLinesWithMultiLineCommentAtEnd() =
this.Parse($"""
module Program

let dog x =
(*
Foo
Bar
*)
%s{generateNewLines (FunctionLength - 4) 4}
(*
Baz
*)
()""")
Assert.IsFalse this.ErrorsExist

[<Test>]
member this.FunctionTooManyLinesWithNestsedMultiLineComment() =
this.Parse($"""
Expand Down Expand Up @@ -354,7 +371,20 @@ let UnionLength = 500
[<TestFixture>]
type TestMaxLinesInUnion() =
inherit TestAstNodeRuleBase.TestAstNodeRuleBase(MaxLinesInUnion.rule { Config.MaxLines = UnionLength })
// TODO: Add tests.

// Test a Union type with an acceptable number of lines, with inline block comments
// This cased used to trip the exception described in https://github.com/fsprojects/FSharpLint/issues/869
// but should be fixed now.
[<Test>]
member this.UnionNotTooManyLines() =
this.Parse """
/// Represents a single group of bindings in a class with an implicit constructor
type IncrClassBindingGroup =
| IncrClassBindingGroup of Tast.Binding list * (*isStatic:*) bool* (*recursive:*) bool
| IncrClassDo of Expr * (*isStatic:*) bool
"""

Assert.IsFalse(this.ErrorExistsAt(4, 5))

[<Literal>]
let RecordLength = 500
Expand Down Expand Up @@ -414,3 +444,75 @@ module Program
let foo = ""
exception SomeException of string""")
Assert.IsFalse(this.ErrorExistsAt(2, 0))

// Tests for 'stripMultilineComments'
// ref https://github.com/fsprojects/FSharpLint/issues/869
[<TestFixture>]
type TestStripMultilineComments() =

[<Test>]
member this.RemoveCommentFromStart() =
let input = """
let dog x =
(*
Foo
Bar
*)
printf System.String.Empty
()"""

let expected = """
let dog x =

printf System.String.Empty
()"""

let actual = FSharpLint.Rules.Helper.SourceLength.stripMultilineComments input
Assert.AreEqual(expected, actual)

[<Test>]
member this.RemoveCommentFromEnd() =
let input = """
let dog x =
printf System.String.Empty
(*
Baz
*)
()"""

let expected = """
let dog x =
printf System.String.Empty

()"""

let actual = FSharpLint.Rules.Helper.SourceLength.stripMultilineComments input
Assert.AreEqual(expected, actual)

[<Test>]
member this.RemoveMultipleComments() =
let input = """
let dog x =
(*
Foo (* baz *)
let (*) = id
Bar
*)
let (*) a b = a + b
printf System.String.Empty
(*
Baz
*)
()"""

let expected = """
let dog x =

let (*) a b = a + b
printf System.String.Empty

()"""

let actual = FSharpLint.Rules.Helper.SourceLength.stripMultilineComments input
Assert.AreEqual(expected, actual)

Loading