diff --git a/src/FSharpLint.Core/FSharpLint.Core.fsproj b/src/FSharpLint.Core/FSharpLint.Core.fsproj
index 05f0be817..7d7c8dd0f 100644
--- a/src/FSharpLint.Core/FSharpLint.Core.fsproj
+++ b/src/FSharpLint.Core/FSharpLint.Core.fsproj
@@ -17,6 +17,8 @@
+
+
diff --git a/src/FSharpLint.Core/Framework/HintParser.fs b/src/FSharpLint.Core/Framework/HintParser.fs
index 1cc333881..672cb616f 100644
--- a/src/FSharpLint.Core/Framework/HintParser.fs
+++ b/src/FSharpLint.Core/Framework/HintParser.fs
@@ -3,389 +3,10 @@
open System
open FParsec
open FSharp.Compiler.Tokenization
+open HintParserTypes
module HintParser =
- type Constant =
- | Byte of byte
- | Bytes of byte[]
- | Char of char
- | Decimal of decimal
- | Double of double
- | Int16 of int16
- | Int32 of int32
- | Int64 of int64
- | IntPtr of nativeint
- | SByte of sbyte
- | Single of single
- | UInt16 of uint16
- | UInt32 of uint32
- | UInt64 of uint64
- | UIntPtr of unativeint
- | UserNum of bigint * char
- | String of string
- | Unit
- | Bool of bool
-
- []
- type Pattern =
- | Cons of Pattern * Pattern
- | Or of Pattern * Pattern
- | Wildcard
- | Variable of char
- | Identifier of string list
- | Constant of Constant
- | Parentheses of Pattern
- | Tuple of Pattern list
- | List of Pattern list
- | Array of Pattern list
- | Null
-
- []
- type Expression =
- | FunctionApplication of Expression list
- | InfixOperator of operatorIdentifier:Expression * Expression * Expression
- | PrefixOperator of operatorIdentifier:Expression * Expression
- | AddressOf of singleAmpersand:bool * Expression
- | Wildcard
- | Variable of char
- | Identifier of string list
- | Constant of Constant
- | Parentheses of Expression
- | Lambda of LambdaArg list * LambdaBody
- | LambdaBody of Expression
- | LambdaArg of Expression
- | Tuple of Expression list
- | List of Expression list
- | Array of Expression list
- | If of cond:Expression * body:Expression * ``else``:Expression option
- | Else of Expression
- | Null
- and LambdaArg = LambdaArg of Expression
- and LambdaBody = LambdaBody of Expression
-
- type HintNode =
- | HintPat of Pattern
- | HintExpr of Expression
-
- type Suggestion =
- | Expr of Expression
- | Message of string
-
- type Hint =
- { MatchedNode:HintNode
- Suggestion:Suggestion }
-
- /// Provides a way of creating a single list from any number of hint ASTs.
- /// Means we can simply iterate over a single list for each node in the F# tree
- /// when matching hints rather than check each hint AST for each node.
- module MergeSyntaxTrees =
-
- open System.Collections.Generic
-
- type SyntaxHintNode =
- | Identifier = 1uy
- | Null = 2uy
- | Expression = 3uy
- | FuncApp = 4uy
- | Unit = 5uy
- | AddressOf = 6uy
-
- | If = 10uy
- | Else = 11uy
-
- | Lambda = 20uy
- | LambdaArg = 21uy
- | LambdaBody = 22uy
-
- | ArrayOrList = 30uy
- | Tuple = 31uy
-
- | Variable = 40uy
- | Wildcard = 41uy
-
- | ConstantBool = 51uy
- | ConstantByte = 52uy
- | ConstantChar = 53uy
- | ConstantDecimal = 54uy
- | ConstantDouble = 55uy
- | ConstantInt16 = 56uy
- | ConstantInt32 = 57uy
- | ConstantInt64 = 58uy
- | ConstantIntPtr = 59uy
- | ConstantSByte = 60uy
- | ConstantSingle = 61uy
- | ConstantString = 62uy
- | ConstantUInt16 = 63uy
- | ConstantUInt32 = 64uy
- | ConstantUInt64 = 65uy
- | ConstantUIntPtr = 66uy
- | ConstantBytes = 67uy
- | ConstantUserNum = 68uy
-
- | Cons = 101uy
- | And = 102uy
- | Or = 103uy
-
- []
- type Node =
- { Edges:Edges
- MatchedHint:Hint list }
- and [] Edges =
- { Lookup:Dictionary
- AnyMatch:(char option * Node) list }
-
- override this.Equals(other) =
- match other with
- | :? Edges as rhs ->
- let getList dict = Seq.toList dict |> List.map (fun (dictItems:KeyValuePair<_, _>) -> (dictItems.Key, dictItems.Value))
-
- this.AnyMatch = rhs.AnyMatch &&
- this.Lookup.Count = rhs.Lookup.Count &&
- getList this.Lookup = getList rhs.Lookup
- | _ -> false
-
- override this.GetHashCode() = hash (this.AnyMatch, hash this.Lookup)
-
- static member Empty = { Lookup = Dictionary<_, _>(); AnyMatch = List.Empty }
-
- let private getConstKey = function
- | Constant.Unit -> SyntaxHintNode.Unit
- | Constant.Bool(_) -> SyntaxHintNode.ConstantBool
- | Constant.Byte(_) -> SyntaxHintNode.ConstantByte
- | Constant.Bytes(_) -> SyntaxHintNode.ConstantBytes
- | Constant.Char(_) -> SyntaxHintNode.ConstantChar
- | Constant.Decimal(_) -> SyntaxHintNode.ConstantDecimal
- | Constant.Double(_) -> SyntaxHintNode.ConstantDouble
- | Constant.Int16(_) -> SyntaxHintNode.ConstantInt16
- | Constant.Int32(_) -> SyntaxHintNode.ConstantInt32
- | Constant.Int64(_) -> SyntaxHintNode.ConstantInt64
- | Constant.IntPtr(_) -> SyntaxHintNode.ConstantIntPtr
- | Constant.SByte(_) -> SyntaxHintNode.ConstantSByte
- | Constant.Single(_) -> SyntaxHintNode.ConstantSingle
- | Constant.String(_) -> SyntaxHintNode.ConstantString
- | Constant.UInt16(_) -> SyntaxHintNode.ConstantUInt16
- | Constant.UInt32(_) -> SyntaxHintNode.ConstantUInt32
- | Constant.UInt64(_) -> SyntaxHintNode.ConstantUInt64
- | Constant.UIntPtr(_) -> SyntaxHintNode.ConstantUIntPtr
- | Constant.UserNum(_) -> SyntaxHintNode.ConstantUserNum
-
- let rec private getExprKey = function
- | Expression.FunctionApplication(_)
- | Expression.InfixOperator(_)
- | Expression.PrefixOperator(_) -> SyntaxHintNode.FuncApp
- | Expression.AddressOf(_) -> SyntaxHintNode.AddressOf
- | Expression.Parentheses(expr) -> getExprKey expr
- | Expression.Lambda(_) -> SyntaxHintNode.Lambda
- | Expression.LambdaArg(_) -> SyntaxHintNode.LambdaArg
- | Expression.LambdaBody(_) -> SyntaxHintNode.LambdaBody
- | Expression.Tuple(_) -> SyntaxHintNode.Tuple
- | Expression.Constant(constant) -> getConstKey constant
- | Expression.List(_)
- | Expression.Array(_) -> SyntaxHintNode.ArrayOrList
- | Expression.If(_) -> SyntaxHintNode.If
- | Expression.Else(_) -> SyntaxHintNode.Else
- | Expression.Identifier(_) -> SyntaxHintNode.Identifier
- | Expression.Null -> SyntaxHintNode.Null
- | Expression.Wildcard -> SyntaxHintNode.Wildcard
- | Expression.Variable(_) -> SyntaxHintNode.Variable
-
- let rec private getPatternKey = function
- | Pattern.Cons(_) -> SyntaxHintNode.Cons
- | Pattern.Or(_) -> SyntaxHintNode.Or
- | Pattern.Wildcard -> SyntaxHintNode.Wildcard
- | Pattern.Variable(_) -> SyntaxHintNode.Variable
- | Pattern.Identifier(_) -> SyntaxHintNode.Identifier
- | Pattern.Constant(constant) -> getConstKey constant
- | Pattern.Parentheses(pattern) -> getPatternKey pattern
- | Pattern.Tuple(_) -> SyntaxHintNode.Tuple
- | Pattern.List(_)
- | Pattern.Array(_) -> SyntaxHintNode.ArrayOrList
- | Pattern.Null -> SyntaxHintNode.Null
-
- let rec private getKey = function
- | HintExpr(expr) -> getExprKey expr
- | HintPat(pattern) -> getPatternKey pattern
-
- let rec private getChildren = function
- | HintExpr(Expression.Parentheses(expr)) -> getChildren <| HintExpr expr
- | HintExpr(Expression.Lambda(args, LambdaBody(body))) ->
- [ for LambdaArg(arg) in args -> HintExpr arg
- yield HintExpr body ]
- | HintExpr(Expression.LambdaArg(arg)) ->
- [HintExpr arg]
- | HintExpr(Expression.LambdaBody(body)) ->
- [HintExpr body]
- | HintExpr(Expression.InfixOperator(Expression.Identifier(["::"]) as ident, lhs, rhs)) ->
- [HintExpr ident; HintExpr (Expression.Tuple([lhs; rhs]))]
- | HintExpr(Expression.InfixOperator(ident, lhs, rhs)) ->
- [HintExpr ident; HintExpr lhs; HintExpr rhs]
- | HintExpr(Expression.PrefixOperator(ident, expr)) ->
- [HintExpr ident; HintExpr expr]
- | HintExpr(Expression.AddressOf(_, expr)) -> [HintExpr expr]
- | HintExpr(Expression.FunctionApplication(exprs))
- | HintExpr(Expression.Tuple(exprs))
- | HintExpr(Expression.List(exprs))
- | HintExpr(Expression.Array(exprs)) -> exprs |> List.map HintExpr
- | HintExpr(Expression.If(ifCond, bodyExpr, Some(elseExpr))) ->
- [HintExpr ifCond; HintExpr bodyExpr; HintExpr elseExpr]
- | HintExpr(Expression.If(ifCond, bodyExpr, None)) ->
- [HintExpr ifCond; HintExpr bodyExpr]
- | HintExpr(Expression.Else(expression)) -> [HintExpr expression]
- | HintExpr(Expression.Identifier(_))
- | HintExpr(Expression.Constant(_))
- | HintExpr(Expression.Null)
- | HintExpr(Expression.Wildcard)
- | HintExpr(Expression.Variable(_)) -> List.Empty
- | HintPat(Pattern.Cons(lhs, rhs))
- | HintPat(Pattern.Or(lhs, rhs)) -> [HintPat lhs; HintPat rhs]
- | HintPat(Pattern.Array(patterns))
- | HintPat(Pattern.List(patterns))
- | HintPat(Pattern.Tuple(patterns)) -> patterns |> List.map HintPat
- | HintPat(Pattern.Parentheses(pattern)) -> [HintPat pattern]
- | HintPat(Pattern.Variable(_))
- | HintPat(Pattern.Identifier(_))
- | HintPat(Pattern.Constant(_))
- | HintPat(Pattern.Wildcard)
- | HintPat(Pattern.Null) -> List.Empty
-
- let private getConstantHashCode = function
- | Constant.Bool value -> hash value
- | Constant.Byte value -> hash value
- | Constant.Bytes value -> hash value
- | Constant.Char value -> hash value
- | Constant.Decimal value -> hash value
- | Constant.Double value -> hash value
- | Constant.Int16 value -> hash value
- | Constant.Int32 value -> hash value
- | Constant.Int64 value -> hash value
- | Constant.IntPtr value -> hash value
- | Constant.SByte value -> hash value
- | Constant.Single value -> hash value
- | Constant.String value -> hash value
- | Constant.UInt16 value -> hash value
- | Constant.UInt32 value -> hash value
- | Constant.UInt64 value -> hash value
- | Constant.UIntPtr value -> hash value
- | Constant.UserNum(intValue, charValue) -> hash (intValue, charValue)
- | _ -> 0
-
- let private getIdentifierHashCode = function
- | identifier when (List.isEmpty >> not) identifier ->
- match (Seq.tryLast identifier) with
- | Some value -> value |> ExpressionUtilities.identAsCompiledOpName |> hash
- | None -> failwith "There's no last element in identifier."
- | _ -> 0
-
- let rec private getHashCode node =
- match node with
- | HintExpr(Expression.Identifier(identifier))
- | HintPat(Pattern.Identifier(identifier)) -> getIdentifierHashCode identifier
- | HintExpr(Expression.Constant(constant))
- | HintPat(Pattern.Constant(constant)) -> getConstantHashCode constant
- | HintExpr(Expression.Parentheses(expr)) -> getHashCode <| HintExpr expr
- | HintPat(Pattern.Parentheses(expr)) -> getHashCode <| HintPat expr
- | _ -> 0
-
- let private hintToList (hint:Hint) =
- let nodes = Queue<_>()
-
- let rec depthFirstTraversal expr depth =
- let children = getChildren expr
-
- nodes.Enqueue(expr, depth)
-
- for child in children do
- depthFirstTraversal child (depth + 1)
-
- depthFirstTraversal hint.MatchedNode 0
-
- (nodes |> Seq.toList, hint)
-
- type private HintList = (HintNode * int) list * Hint
-
- type private TransposedNode =
- | HintNode of key:HintNode * depth:int * rest:HintList
- | EndOfHint of Hint
-
- /// Gets the head of each given list
- let private transposeHead hintLists =
- let rec transposeHead builtList = function
- | (((key, depth)::tail), hint)::rest ->
- let restOfHintList = (tail, hint)
- let next = HintNode(key, depth, restOfHintList)::builtList
- transposeHead next rest
- | ([], hint)::rest ->
- let next = EndOfHint(hint)::builtList
- transposeHead next rest
- | [] -> builtList
-
- transposeHead List.Empty hintLists
-
- let isAnyMatch = function
- | ((SyntaxHintNode.Wildcard | SyntaxHintNode.Variable), _, _, _) -> true
- | _ -> false
-
- let getHints items = items |> Seq.map (fun (_, _, _, hint) -> hint) |> Seq.toList
-
- let mergeHints hints =
- let rec getEdges transposed =
- let map = Dictionary<_, _>()
-
- transposed
- |> List.choose (function
- | HintNode(expr, depth, rest) -> Some(getKey expr, expr, depth, rest)
- | EndOfHint(_) -> None)
- |> List.filter (isAnyMatch >> not)
- |> Seq.groupBy (fun (key, expr, _, _) -> Utilities.hash2 key (getHashCode expr))
- |> Seq.iter (fun (hashcode, items) -> map.Add(hashcode, mergeHints (getHints items)))
-
- let anyMatches =
- transposed
- |> List.choose (function
- | HintNode(expr, depth, rest) ->
- match (getKey expr, expr) with
- | (SyntaxHintNode.Wildcard as key), HintExpr(Expression.Wildcard)
- | (SyntaxHintNode.Wildcard as key), HintPat(Pattern.Wildcard)
- | (SyntaxHintNode.Variable as key), HintExpr(Expression.Variable(_))
- | (SyntaxHintNode.Variable as key), HintPat(Pattern.Variable(_)) ->
- Some(key, expr, depth, rest)
- | _ -> None
- | EndOfHint(_) -> None)
- |> Seq.groupBy (fun (_, expr, _, _) -> expr)
- |> Seq.choose
- (fun (expr, items) ->
- match expr with
- | HintPat(Pattern.Wildcard)
- | HintExpr(Expression.Wildcard) -> Some(None, mergeHints (getHints items))
- | HintPat(Pattern.Variable(var))
- | HintExpr(Expression.Variable(var)) -> Some(Some(var), mergeHints (getHints items))
- | _ -> None)
- |> Seq.toList
-
- { Lookup = map
- AnyMatch = anyMatches }
-
- and mergeHints hints =
- let transposed = transposeHead hints
-
- let edges = getEdges transposed
-
- let matchedHints =
- transposed
- |> Seq.choose (function
- | HintNode(_) -> None
- | EndOfHint(hint) -> Some(hint))
- |> Seq.toList
-
- { Edges = edges
- MatchedHint = matchedHints }
-
- let transposed =
- hints |> List.map hintToList |> transposeHead
-
- getEdges transposed
-
let charListToString charList =
Seq.fold (fun concatenatedString charElement -> concatenatedString + charElement.ToString()) String.Empty charList
diff --git a/src/FSharpLint.Core/Framework/HintParserTypes.fs b/src/FSharpLint.Core/Framework/HintParserTypes.fs
new file mode 100644
index 000000000..5ba73a328
--- /dev/null
+++ b/src/FSharpLint.Core/Framework/HintParserTypes.fs
@@ -0,0 +1,73 @@
+namespace FSharpLint.Framework
+
+module HintParserTypes =
+
+ type Constant =
+ | Byte of byte
+ | Bytes of byte[]
+ | Char of char
+ | Decimal of decimal
+ | Double of double
+ | Int16 of int16
+ | Int32 of int32
+ | Int64 of int64
+ | IntPtr of nativeint
+ | SByte of sbyte
+ | Single of single
+ | UInt16 of uint16
+ | UInt32 of uint32
+ | UInt64 of uint64
+ | UIntPtr of unativeint
+ | UserNum of bigint * char
+ | String of string
+ | Unit
+ | Bool of bool
+
+ []
+ type Pattern =
+ | Cons of Pattern * Pattern
+ | Or of Pattern * Pattern
+ | Wildcard
+ | Variable of char
+ | Identifier of string list
+ | Constant of Constant
+ | Parentheses of Pattern
+ | Tuple of Pattern list
+ | List of Pattern list
+ | Array of Pattern list
+ | Null
+
+ []
+ type Expression =
+ | FunctionApplication of Expression list
+ | InfixOperator of operatorIdentifier:Expression * Expression * Expression
+ | PrefixOperator of operatorIdentifier:Expression * Expression
+ | AddressOf of singleAmpersand:bool * Expression
+ | Wildcard
+ | Variable of char
+ | Identifier of string list
+ | Constant of Constant
+ | Parentheses of Expression
+ | Lambda of LambdaArg list * LambdaBody
+ | LambdaBody of Expression
+ | LambdaArg of Expression
+ | Tuple of Expression list
+ | List of Expression list
+ | Array of Expression list
+ | If of cond:Expression * body:Expression * ``else``:Expression option
+ | Else of Expression
+ | Null
+ and LambdaArg = LambdaArg of Expression
+ and LambdaBody = LambdaBody of Expression
+
+ type HintNode =
+ | HintPat of Pattern
+ | HintExpr of Expression
+
+ type Suggestion =
+ | Expr of Expression
+ | Message of string
+
+ type Hint =
+ { MatchedNode:HintNode
+ Suggestion:Suggestion }
\ No newline at end of file
diff --git a/src/FSharpLint.Core/Framework/HintParserUtilities.fs b/src/FSharpLint.Core/Framework/HintParserUtilities.fs
new file mode 100644
index 000000000..0ef15015f
--- /dev/null
+++ b/src/FSharpLint.Core/Framework/HintParserUtilities.fs
@@ -0,0 +1,313 @@
+namespace FSharpLint.Framework
+
+open HintParserTypes
+
+/// Provides a way of creating a single list from any number of hint ASTs.
+/// Means we can simply iterate over a single list for each node in the F# tree
+/// when matching hints rather than check each hint AST for each node.
+module MergeSyntaxTrees =
+
+ open System.Collections.Generic
+
+ type SyntaxHintNode =
+ | Identifier = 1uy
+ | Null = 2uy
+ | Expression = 3uy
+ | FuncApp = 4uy
+ | Unit = 5uy
+ | AddressOf = 6uy
+
+ | If = 10uy
+ | Else = 11uy
+
+ | Lambda = 20uy
+ | LambdaArg = 21uy
+ | LambdaBody = 22uy
+
+ | ArrayOrList = 30uy
+ | Tuple = 31uy
+
+ | Variable = 40uy
+ | Wildcard = 41uy
+
+ | ConstantBool = 51uy
+ | ConstantByte = 52uy
+ | ConstantChar = 53uy
+ | ConstantDecimal = 54uy
+ | ConstantDouble = 55uy
+ | ConstantInt16 = 56uy
+ | ConstantInt32 = 57uy
+ | ConstantInt64 = 58uy
+ | ConstantIntPtr = 59uy
+ | ConstantSByte = 60uy
+ | ConstantSingle = 61uy
+ | ConstantString = 62uy
+ | ConstantUInt16 = 63uy
+ | ConstantUInt32 = 64uy
+ | ConstantUInt64 = 65uy
+ | ConstantUIntPtr = 66uy
+ | ConstantBytes = 67uy
+ | ConstantUserNum = 68uy
+
+ | Cons = 101uy
+ | And = 102uy
+ | Or = 103uy
+
+ []
+ type Node =
+ { Edges:Edges
+ MatchedHint:Hint list }
+ and [] Edges =
+ { Lookup:Dictionary
+ AnyMatch:(char option * Node) list }
+
+ override this.Equals(other) =
+ match other with
+ | :? Edges as rhs ->
+ let getList dict = Seq.toList dict |> List.map (fun (dictItems:KeyValuePair<_, _>) -> (dictItems.Key, dictItems.Value))
+
+ this.AnyMatch = rhs.AnyMatch &&
+ this.Lookup.Count = rhs.Lookup.Count &&
+ getList this.Lookup = getList rhs.Lookup
+ | _ -> false
+
+ override this.GetHashCode() = hash (this.AnyMatch, hash this.Lookup)
+
+ static member Empty = { Lookup = Dictionary<_, _>(); AnyMatch = List.Empty }
+
+ let private getConstKey = function
+ | Constant.Unit -> SyntaxHintNode.Unit
+ | Constant.Bool(_) -> SyntaxHintNode.ConstantBool
+ | Constant.Byte(_) -> SyntaxHintNode.ConstantByte
+ | Constant.Bytes(_) -> SyntaxHintNode.ConstantBytes
+ | Constant.Char(_) -> SyntaxHintNode.ConstantChar
+ | Constant.Decimal(_) -> SyntaxHintNode.ConstantDecimal
+ | Constant.Double(_) -> SyntaxHintNode.ConstantDouble
+ | Constant.Int16(_) -> SyntaxHintNode.ConstantInt16
+ | Constant.Int32(_) -> SyntaxHintNode.ConstantInt32
+ | Constant.Int64(_) -> SyntaxHintNode.ConstantInt64
+ | Constant.IntPtr(_) -> SyntaxHintNode.ConstantIntPtr
+ | Constant.SByte(_) -> SyntaxHintNode.ConstantSByte
+ | Constant.Single(_) -> SyntaxHintNode.ConstantSingle
+ | Constant.String(_) -> SyntaxHintNode.ConstantString
+ | Constant.UInt16(_) -> SyntaxHintNode.ConstantUInt16
+ | Constant.UInt32(_) -> SyntaxHintNode.ConstantUInt32
+ | Constant.UInt64(_) -> SyntaxHintNode.ConstantUInt64
+ | Constant.UIntPtr(_) -> SyntaxHintNode.ConstantUIntPtr
+ | Constant.UserNum(_) -> SyntaxHintNode.ConstantUserNum
+
+ let rec private getExprKey = function
+ | Expression.FunctionApplication(_)
+ | Expression.InfixOperator(_)
+ | Expression.PrefixOperator(_) -> SyntaxHintNode.FuncApp
+ | Expression.AddressOf(_) -> SyntaxHintNode.AddressOf
+ | Expression.Parentheses(expr) -> getExprKey expr
+ | Expression.Lambda(_) -> SyntaxHintNode.Lambda
+ | Expression.LambdaArg(_) -> SyntaxHintNode.LambdaArg
+ | Expression.LambdaBody(_) -> SyntaxHintNode.LambdaBody
+ | Expression.Tuple(_) -> SyntaxHintNode.Tuple
+ | Expression.Constant(constant) -> getConstKey constant
+ | Expression.List(_)
+ | Expression.Array(_) -> SyntaxHintNode.ArrayOrList
+ | Expression.If(_) -> SyntaxHintNode.If
+ | Expression.Else(_) -> SyntaxHintNode.Else
+ | Expression.Identifier(_) -> SyntaxHintNode.Identifier
+ | Expression.Null -> SyntaxHintNode.Null
+ | Expression.Wildcard -> SyntaxHintNode.Wildcard
+ | Expression.Variable(_) -> SyntaxHintNode.Variable
+
+ let rec private getPatternKey = function
+ | Pattern.Cons(_) -> SyntaxHintNode.Cons
+ | Pattern.Or(_) -> SyntaxHintNode.Or
+ | Pattern.Wildcard -> SyntaxHintNode.Wildcard
+ | Pattern.Variable(_) -> SyntaxHintNode.Variable
+ | Pattern.Identifier(_) -> SyntaxHintNode.Identifier
+ | Pattern.Constant(constant) -> getConstKey constant
+ | Pattern.Parentheses(pattern) -> getPatternKey pattern
+ | Pattern.Tuple(_) -> SyntaxHintNode.Tuple
+ | Pattern.List(_)
+ | Pattern.Array(_) -> SyntaxHintNode.ArrayOrList
+ | Pattern.Null -> SyntaxHintNode.Null
+
+ let rec private getKey = function
+ | HintExpr(expr) -> getExprKey expr
+ | HintPat(pattern) -> getPatternKey pattern
+
+ let rec private getChildren = function
+ | HintExpr(Expression.Parentheses(expr)) -> getChildren <| HintExpr expr
+ | HintExpr(Expression.Lambda(args, LambdaBody(body))) ->
+ [ for LambdaArg(arg) in args -> HintExpr arg
+ yield HintExpr body ]
+ | HintExpr(Expression.LambdaArg(arg)) ->
+ [HintExpr arg]
+ | HintExpr(Expression.LambdaBody(body)) ->
+ [HintExpr body]
+ | HintExpr(Expression.InfixOperator(Expression.Identifier(["::"]) as ident, lhs, rhs)) ->
+ [HintExpr ident; HintExpr (Expression.Tuple([lhs; rhs]))]
+ | HintExpr(Expression.InfixOperator(ident, lhs, rhs)) ->
+ [HintExpr ident; HintExpr lhs; HintExpr rhs]
+ | HintExpr(Expression.PrefixOperator(ident, expr)) ->
+ [HintExpr ident; HintExpr expr]
+ | HintExpr(Expression.AddressOf(_, expr)) -> [HintExpr expr]
+ | HintExpr(Expression.FunctionApplication(exprs))
+ | HintExpr(Expression.Tuple(exprs))
+ | HintExpr(Expression.List(exprs))
+ | HintExpr(Expression.Array(exprs)) -> exprs |> List.map HintExpr
+ | HintExpr(Expression.If(ifCond, bodyExpr, Some(elseExpr))) ->
+ [HintExpr ifCond; HintExpr bodyExpr; HintExpr elseExpr]
+ | HintExpr(Expression.If(ifCond, bodyExpr, None)) ->
+ [HintExpr ifCond; HintExpr bodyExpr]
+ | HintExpr(Expression.Else(expression)) -> [HintExpr expression]
+ | HintExpr(Expression.Identifier(_))
+ | HintExpr(Expression.Constant(_))
+ | HintExpr(Expression.Null)
+ | HintExpr(Expression.Wildcard)
+ | HintExpr(Expression.Variable(_)) -> List.Empty
+ | HintPat(Pattern.Cons(lhs, rhs))
+ | HintPat(Pattern.Or(lhs, rhs)) -> [HintPat lhs; HintPat rhs]
+ | HintPat(Pattern.Array(patterns))
+ | HintPat(Pattern.List(patterns))
+ | HintPat(Pattern.Tuple(patterns)) -> patterns |> List.map HintPat
+ | HintPat(Pattern.Parentheses(pattern)) -> [HintPat pattern]
+ | HintPat(Pattern.Variable(_))
+ | HintPat(Pattern.Identifier(_))
+ | HintPat(Pattern.Constant(_))
+ | HintPat(Pattern.Wildcard)
+ | HintPat(Pattern.Null) -> List.Empty
+
+ let private getConstantHashCode = function
+ | Constant.Bool value -> hash value
+ | Constant.Byte value -> hash value
+ | Constant.Bytes value -> hash value
+ | Constant.Char value -> hash value
+ | Constant.Decimal value -> hash value
+ | Constant.Double value -> hash value
+ | Constant.Int16 value -> hash value
+ | Constant.Int32 value -> hash value
+ | Constant.Int64 value -> hash value
+ | Constant.IntPtr value -> hash value
+ | Constant.SByte value -> hash value
+ | Constant.Single value -> hash value
+ | Constant.String value -> hash value
+ | Constant.UInt16 value -> hash value
+ | Constant.UInt32 value -> hash value
+ | Constant.UInt64 value -> hash value
+ | Constant.UIntPtr value -> hash value
+ | Constant.UserNum(intValue, charValue) -> hash (intValue, charValue)
+ | _ -> 0
+
+ let private getIdentifierHashCode = function
+ | identifier when (List.isEmpty >> not) identifier ->
+ match (Seq.tryLast identifier) with
+ | Some value -> value |> ExpressionUtilities.identAsCompiledOpName |> hash
+ | None -> failwith "There's no last element in identifier."
+ | _ -> 0
+
+ let rec private getHashCode node =
+ match node with
+ | HintExpr(Expression.Identifier(identifier))
+ | HintPat(Pattern.Identifier(identifier)) -> getIdentifierHashCode identifier
+ | HintExpr(Expression.Constant(constant))
+ | HintPat(Pattern.Constant(constant)) -> getConstantHashCode constant
+ | HintExpr(Expression.Parentheses(expr)) -> getHashCode <| HintExpr expr
+ | HintPat(Pattern.Parentheses(expr)) -> getHashCode <| HintPat expr
+ | _ -> 0
+
+ let private hintToList (hint:Hint) =
+ let nodes = Queue<_>()
+
+ let rec depthFirstTraversal expr depth =
+ let children = getChildren expr
+
+ nodes.Enqueue(expr, depth)
+
+ for child in children do
+ depthFirstTraversal child (depth + 1)
+
+ depthFirstTraversal hint.MatchedNode 0
+
+ (nodes |> Seq.toList, hint)
+
+ type private HintList = (HintNode * int) list * Hint
+
+ type private TransposedNode =
+ | HintNode of key:HintNode * depth:int * rest:HintList
+ | EndOfHint of Hint
+
+ /// Gets the head of each given list
+ let private transposeHead hintLists =
+ let rec transposeHead builtList = function
+ | (((key, depth)::tail), hint)::rest ->
+ let restOfHintList = (tail, hint)
+ let next = HintNode(key, depth, restOfHintList)::builtList
+ transposeHead next rest
+ | ([], hint)::rest ->
+ let next = EndOfHint(hint)::builtList
+ transposeHead next rest
+ | [] -> builtList
+
+ transposeHead List.Empty hintLists
+
+ let isAnyMatch = function
+ | ((SyntaxHintNode.Wildcard | SyntaxHintNode.Variable), _, _, _) -> true
+ | _ -> false
+
+ let getHints items = items |> Seq.map (fun (_, _, _, hint) -> hint) |> Seq.toList
+
+ let mergeHints hints =
+ let rec getEdges transposed =
+ let map = Dictionary<_, _>()
+
+ transposed
+ |> List.choose (function
+ | HintNode(expr, depth, rest) -> Some(getKey expr, expr, depth, rest)
+ | EndOfHint(_) -> None)
+ |> List.filter (isAnyMatch >> not)
+ |> Seq.groupBy (fun (key, expr, _, _) -> Utilities.hash2 key (getHashCode expr))
+ |> Seq.iter (fun (hashcode, items) -> map.Add(hashcode, mergeHints (getHints items)))
+
+ let anyMatches =
+ transposed
+ |> List.choose (function
+ | HintNode(expr, depth, rest) ->
+ match (getKey expr, expr) with
+ | (SyntaxHintNode.Wildcard as key), HintExpr(Expression.Wildcard)
+ | (SyntaxHintNode.Wildcard as key), HintPat(Pattern.Wildcard)
+ | (SyntaxHintNode.Variable as key), HintExpr(Expression.Variable(_))
+ | (SyntaxHintNode.Variable as key), HintPat(Pattern.Variable(_)) ->
+ Some(key, expr, depth, rest)
+ | _ -> None
+ | EndOfHint(_) -> None)
+ |> Seq.groupBy (fun (_, expr, _, _) -> expr)
+ |> Seq.choose
+ (fun (expr, items) ->
+ match expr with
+ | HintPat(Pattern.Wildcard)
+ | HintExpr(Expression.Wildcard) -> Some(None, mergeHints (getHints items))
+ | HintPat(Pattern.Variable(var))
+ | HintExpr(Expression.Variable(var)) -> Some(Some(var), mergeHints (getHints items))
+ | _ -> None)
+ |> Seq.toList
+
+ { Lookup = map
+ AnyMatch = anyMatches }
+
+ and mergeHints hints =
+ let transposed = transposeHead hints
+
+ let edges = getEdges transposed
+
+ let matchedHints =
+ transposed
+ |> Seq.choose (function
+ | HintNode(_) -> None
+ | EndOfHint(hint) -> Some(hint))
+ |> Seq.toList
+
+ { Edges = edges
+ MatchedHint = matchedHints }
+
+ let transposed =
+ hints |> List.map hintToList |> transposeHead
+
+ getEdges transposed
diff --git a/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs b/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs
index b8cfe2127..de0f9fbca 100644
--- a/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs
+++ b/src/FSharpLint.Core/Rules/Hints/HintMatcher.fs
@@ -12,6 +12,7 @@ open FSharpLint.Framework.Ast
open FSharpLint.Framework.ExpressionUtilities
open FSharpLint.Framework.HintParser
open FSharpLint.Framework.Rules
+open FSharpLint.Framework.HintParserTypes
type ToStringConfig =
{
@@ -62,7 +63,7 @@ type private LambdaMatch =
| Match of Map
| NoMatch
-let private matchLambdaArguments (hintArgs:HintParser.LambdaArg list) (actualArgs:SynSimplePats list) =
+let private matchLambdaArguments (hintArgs:HintParserTypes.LambdaArg list) (actualArgs:SynSimplePats list) =
if List.length hintArgs <> List.length actualArgs then
LambdaMatch.NoMatch
else
@@ -704,7 +705,7 @@ let private (|SuggestingReplacementOfLambda|OtherSuggestion|) = function
let [] private MaxBreadcrumbs = 6
let private suggestions = ResizeArray()
-let private confirmFuzzyMatch (args:AstNodeRuleParams) (hint:HintParser.Hint) =
+let private confirmFuzzyMatch (args:AstNodeRuleParams) (hint:HintParserTypes.Hint) =
let breadcrumbs = args.GetParents MaxBreadcrumbs
match (args.AstNode, hint.MatchedNode) with
| AstNode.Expression(SynExpr.Paren(_)), HintExpr(_)
diff --git a/tests/FSharpLint.Core.Tests/Framework/TestFuzzyHintMatcher.fs b/tests/FSharpLint.Core.Tests/Framework/TestFuzzyHintMatcher.fs
index 3a539ddae..dd32c617f 100644
--- a/tests/FSharpLint.Core.Tests/Framework/TestFuzzyHintMatcher.fs
+++ b/tests/FSharpLint.Core.Tests/Framework/TestFuzzyHintMatcher.fs
@@ -6,7 +6,7 @@ open FSharpLint.Rules.Helper.Hints
open FSharpLint.Framework
open FSharpLint.Framework.AbstractSyntaxArray
open FSharpLint.Framework.HintParser
-open FSharpLint.Framework.HintParser.MergeSyntaxTrees
+open FSharpLint.Framework.MergeSyntaxTrees
open NUnit.Framework
open FParsec
open TestUtils
diff --git a/tests/FSharpLint.Core.Tests/Framework/TestHintParser.fs b/tests/FSharpLint.Core.Tests/Framework/TestHintParser.fs
index 6ff858a83..d979325ce 100644
--- a/tests/FSharpLint.Core.Tests/Framework/TestHintParser.fs
+++ b/tests/FSharpLint.Core.Tests/Framework/TestHintParser.fs
@@ -6,7 +6,8 @@ open NUnit.Framework
open FSharpLint.Framework
open FSharpLint.Framework.HintParser
open FParsec
-open MergeSyntaxTrees
+open FSharpLint.Framework.MergeSyntaxTrees
+open FSharpLint.Framework.HintParserTypes
open System.Collections.Generic
[]
diff --git a/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs b/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs
index e8b293de2..2461d5bd6 100644
--- a/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs
+++ b/tests/FSharpLint.Core.Tests/Rules/TestHintMatcherBase.fs
@@ -6,7 +6,7 @@ open FSharp.Compiler.CodeAnalysis
open FSharpLint.Application
open FSharpLint.Framework
open FSharpLint.Framework.HintParser
-open FSharpLint.Framework.HintParser.MergeSyntaxTrees
+open FSharpLint.Framework.MergeSyntaxTrees
open FSharpLint.Framework.ParseFile
open FSharpLint.Rules