Skip to content

Commit

Permalink
Core: fix FSharpLint warning
Browse files Browse the repository at this point in the history
Fixing the warning for maxNumberOfFunctionParameters rule.
  • Loading branch information
Mersho committed Jan 15, 2024
1 parent 122ac97 commit 73ad48f
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 77 deletions.
88 changes: 59 additions & 29 deletions src/FSharpLint.Core/Application/Lint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -118,67 +118,81 @@ module Lint =
{ IndentationRuleContext:Map<int,bool*int>
NoTabCharactersRuleContext:(string * Range) list }

let runAstNodeRules (rules:RuleMetadata<AstNodeRuleConfig> []) (globalConfig:Rules.GlobalRuleConfig) typeCheckResults (filePath:string) (fileContent:string) (lines:string []) syntaxArray =
type RunAstNodeRulesConfig =
{ Rules: RuleMetadata<AstNodeRuleConfig> []
GlobalConfig: Rules.GlobalRuleConfig
TypeCheckResults: FSharpCheckFileResults option
FilePath: string
FileContent: string
Lines: string []
SyntaxArray: AbstractSyntaxArray.Node array }

let runAstNodeRules (config: RunAstNodeRulesConfig) =
let mutable indentationRuleState = Map.empty
let mutable noTabCharactersRuleState = List.empty

let collect index (astNode: AbstractSyntaxArray.Node) =
let getParents (depth:int) = AbstractSyntaxArray.getBreadcrumbs depth syntaxArray index
let getParents (depth:int) = AbstractSyntaxArray.getBreadcrumbs depth config.SyntaxArray index
let astNodeParams =
{
AstNode = astNode.Actual
NodeHashcode = astNode.Hashcode
NodeIndex = index
SyntaxArray = syntaxArray
SyntaxArray = config.SyntaxArray
GetParents = getParents
FilePath = filePath
FileContent = fileContent
Lines = lines
CheckInfo = typeCheckResults
GlobalConfig = globalConfig }
FilePath = config.FilePath
FileContent = config.FileContent
Lines = config.Lines
CheckInfo = config.TypeCheckResults
GlobalConfig = config.GlobalConfig }
// Build state for rules with context.
indentationRuleState <- Indentation.ContextBuilder.builder indentationRuleState astNode.Actual
noTabCharactersRuleState <- NoTabCharacters.ContextBuilder.builder noTabCharactersRuleState astNode.Actual

rules
config.Rules
|> Array.collect (fun rule -> runAstNodeRule rule astNodeParams)

// Collect suggestions for AstNode rules, and build context for following rules.
let astNodeSuggestions =
syntaxArray
config.SyntaxArray
|> Array.mapi (fun index astNode -> (index, astNode))
|> Array.collect (fun (index, astNode) -> collect index astNode)

let context =
{ IndentationRuleContext = indentationRuleState
NoTabCharactersRuleContext = noTabCharactersRuleState }

rules |> Array.iter (fun rule -> rule.RuleConfig.Cleanup())
config.Rules |> Array.iter (fun rule -> rule.RuleConfig.Cleanup())
(astNodeSuggestions, context)

let runLineRules (lineRules:Configuration.LineRules) (globalConfig:Rules.GlobalRuleConfig) (filePath:string) (fileContent:string) (lines:string []) (context:Context) =
type RunLineRulesConfig =
{ LineRules: Configuration.LineRules
GlobalConfig: Rules.GlobalRuleConfig
FilePath: string
FileContent: string
Lines: string[]
Context: Context }
let runLineRules (config: RunLineRulesConfig) =
let collectErrors (line: string) (lineNumber: int) (isLastLine: bool) =
let lineParams =
{
LineRuleParams.Line = line
LineNumber = lineNumber + 1
IsLastLine = isLastLine
FilePath = filePath
FileContent = fileContent
Lines = lines
GlobalConfig = globalConfig
}
{ LineRuleParams.Line = line
LineNumber = lineNumber + 1
IsLastLine = isLastLine
FilePath = config.FilePath
FileContent = config.FileContent
Lines = config.Lines
GlobalConfig = config.GlobalConfig }

let indentationError =
lineRules.IndentationRule
|> Option.map (fun rule -> runLineRuleWithContext rule context.IndentationRuleContext lineParams)
config.LineRules.IndentationRule
|> Option.map (fun rule -> runLineRuleWithContext rule config.Context.IndentationRuleContext lineParams)

let noTabCharactersError =
lineRules.NoTabCharactersRule
|> Option.map (fun rule -> runLineRuleWithContext rule context.NoTabCharactersRuleContext lineParams)
config.LineRules.NoTabCharactersRule
|> Option.map (fun rule -> runLineRuleWithContext rule config.Context.NoTabCharactersRuleContext lineParams)

let lineErrors =
lineRules.GenericLineRules
config.LineRules.GenericLineRules
|> Array.collect (fun rule -> runLineRule rule lineParams)

[|
Expand All @@ -187,7 +201,7 @@ module Lint =
lineErrors |> Array.singleton
|]

fileContent
config.FileContent
|> String.toLines
|> Array.collect (fun (line, lineNumber, isLastLine) -> collectErrors line lineNumber isLastLine)
|> Array.concat
Expand Down Expand Up @@ -230,8 +244,24 @@ module Lint =
let syntaxArray = AbstractSyntaxArray.astToArray fileInfo.Ast

// Collect suggestions for AstNode rules
let (astNodeSuggestions, context) = runAstNodeRules enabledRules.AstNodeRules enabledRules.GlobalConfig fileInfo.TypeCheckResults fileInfo.File fileInfo.Text lines syntaxArray
let lineSuggestions = runLineRules enabledRules.LineRules enabledRules.GlobalConfig fileInfo.File fileInfo.Text lines context
let (astNodeSuggestions, context) =
runAstNodeRules
{ Rules = enabledRules.AstNodeRules
GlobalConfig = enabledRules.GlobalConfig
TypeCheckResults = fileInfo.TypeCheckResults
FilePath = fileInfo.File
FileContent = fileInfo.Text
Lines = lines
SyntaxArray = syntaxArray }

let lineSuggestions =
runLineRules
{ LineRules = enabledRules.LineRules
GlobalConfig = enabledRules.GlobalConfig
FilePath = fileInfo.File
FileContent = fileInfo.Text
Lines = lines
Context = context }

[| lineSuggestions; astNodeSuggestions |]
|> Array.concat
Expand Down
21 changes: 19 additions & 2 deletions src/FSharpLint.Core/Application/Lint.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,28 @@ module Lint =
member TryGetSuccess : byref<Suggestion.LintWarning list> -> bool
member TryGetFailure : byref<LintFailure> -> bool

type RunAstNodeRulesConfig =
{ Rules: RuleMetadata<AstNodeRuleConfig> []
GlobalConfig: Rules.GlobalRuleConfig
TypeCheckResults: FSharpCheckFileResults option
FilePath: string
FileContent: string
Lines: string []
SyntaxArray: AbstractSyntaxArray.Node array }

/// Runs all rules which take a node of the AST as input.
val runAstNodeRules : RuleMetadata<AstNodeRuleConfig> [] -> Rules.GlobalRuleConfig -> FSharpCheckFileResults option -> string -> string -> string [] -> AbstractSyntaxArray.Node [] -> Suggestion.LintWarning [] * Context
val runAstNodeRules : RunAstNodeRulesConfig -> Suggestion.LintWarning [] * Context

type RunLineRulesConfig =
{ LineRules: Configuration.LineRules
GlobalConfig: Rules.GlobalRuleConfig
FilePath: string
FileContent: string
Lines: string[]
Context: Context }

/// Runs all rules which take a line of text as input.
val runLineRules : LineRules -> Rules.GlobalRuleConfig -> string -> string -> string [] -> Context -> Suggestion.LintWarning []
val runLineRules : RunLineRulesConfig -> Suggestion.LintWarning []

/// Lints an entire F# solution by linting all projects specified in the `.sln` file.
val lintSolution : optionalParams:OptionalLintParameters -> solutionFilePath:string -> toolsPath:Ionide.ProjInfo.Types.ToolsPath -> LintResult
Expand Down
48 changes: 31 additions & 17 deletions src/FSharpLint.Core/Rules/Formatting/TypePrefixing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@ type Mode =
[<RequireQualifiedAccess>]
type Config = { Mode: Mode }

let checkTypePrefixing (config:Config) (args:AstNodeRuleParams) range typeName typeArgs isPostfix =
type CheckTypePrefixingConfig =
{ Config: Config
Args: AstNodeRuleParams
Range: FSharp.Compiler.Text.Range
TypeName: SynType
TypeArgs: string option
IsPostfix: bool }

let checkTypePrefixing (typePrefixingConfig: CheckTypePrefixingConfig) =
let recommendPostfixErrMsg = lazy(Resources.GetString("RulesFormattingF#PostfixGenericError"))
match typeName with
match typePrefixingConfig.TypeName with
| SynType.LongIdent lid ->
let prefixSuggestion typeName =
let suggestedFix = lazy(
(ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeName + "<" + typeArgs + ">" }))
{ Range = range
(ExpressionUtilities.tryFindTextOfRange typePrefixingConfig.Range typePrefixingConfig.Args.FileContent, typePrefixingConfig.TypeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = typePrefixingConfig.Range; ToText = typeName + "<" + typeArgs + ">" }))
{ Range = typePrefixingConfig.Range
Message = Resources.GetString("RulesFormattingGenericPrefixError")
SuggestedFix = Some suggestedFix
TypeChecks = [] } |> Some
Expand All @@ -38,39 +46,39 @@ let checkTypePrefixing (config:Config) (args:AstNodeRuleParams) range typeName t
| "Ref" as typeName ->

// Prefer postfix.
if not isPostfix && config.Mode <> Mode.Always
if not typePrefixingConfig.IsPostfix && typePrefixingConfig.Config.Mode <> Mode.Always
then
let suggestedFix = lazy(
(ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeArgs + " " + typeName }))
{ Range = range
(ExpressionUtilities.tryFindTextOfRange typePrefixingConfig.Range typePrefixingConfig.Args.FileContent, typePrefixingConfig.TypeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = typePrefixingConfig.Range; ToText = typeArgs + " " + typeName }))
{ Range = typePrefixingConfig.Range
Message = String.Format(recommendPostfixErrMsg.Value, typeName)
SuggestedFix = Some suggestedFix
TypeChecks = [] } |> Some
else
if isPostfix && config.Mode = Mode.Always then
if typePrefixingConfig.IsPostfix && typePrefixingConfig.Config.Mode = Mode.Always then
prefixSuggestion typeName
else
None

| "array" when config.Mode <> Mode.Always ->
| "array" when typePrefixingConfig.Config.Mode <> Mode.Always ->
// Prefer special postfix (e.g. int []).
let suggestedFix = lazy(
(ExpressionUtilities.tryFindTextOfRange range args.FileContent, typeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = range; ToText = typeArgs + " []" }))
{ Range = range
(ExpressionUtilities.tryFindTextOfRange typePrefixingConfig.Range typePrefixingConfig.Args.FileContent, typePrefixingConfig.TypeArgs)
||> Option.map2 (fun fromText typeArgs -> { FromText = fromText; FromRange = typePrefixingConfig.Range; ToText = typeArgs + " []" }))
{ Range = typePrefixingConfig.Range
Message = Resources.GetString("RulesFormattingF#ArrayPostfixError")
SuggestedFix = Some suggestedFix
TypeChecks = [] } |> Some

| typeName ->
match (isPostfix, config.Mode) with
match (typePrefixingConfig.IsPostfix, typePrefixingConfig.Config.Mode) with
| true, Mode.Never ->
None
| true, _ ->
prefixSuggestion typeName
| false, Mode.Never ->
{ Range = range
{ Range = typePrefixingConfig.Range
Message = String.Format(recommendPostfixErrMsg.Value, typeName)
// TODO
SuggestedFix = None
Expand All @@ -84,7 +92,13 @@ let runner (config:Config) args =
match args.AstNode with
| AstNode.Type (SynType.App (typeName, _, typeArgs, _, _, isPostfix, range)) ->
let typeArgs = typeArgsToString args.FileContent typeArgs
checkTypePrefixing config args range typeName typeArgs isPostfix
checkTypePrefixing
{ Config = config
Args = args
Range = range
TypeName = typeName
TypeArgs = typeArgs
IsPostfix = isPostfix }
|> Option.toArray
| AstNode.Type (SynType.Array (1, _elementType, range)) when config.Mode = Mode.Always ->
{ Range = range
Expand Down
Loading

0 comments on commit 73ad48f

Please sign in to comment.