Report a global error when an import cycle is found

This commit is contained in:
Jeroen Engels 2020-02-13 18:11:48 +01:00
parent c01be0f6fe
commit 23632062ca

View File

@ -234,7 +234,7 @@ TODO Link to "creating a module rule" and project rule instead
See [`newModuleRuleSchema`](#newModuleRuleSchema), and [`newProjectRuleSchema`](#newProjectRuleSchema) for how to create one. See [`newModuleRuleSchema`](#newModuleRuleSchema), and [`newProjectRuleSchema`](#newProjectRuleSchema) for how to create one.
-} -}
type Rule type Rule
= Rule String (Project -> ( List Error, Rule )) = Rule String (Project -> List (Graph.NodeContext ModuleName ()) -> ( List Error, Rule ))
{-| Represents a schema for a module [`Rule`](#Rule). {-| Represents a schema for a module [`Rule`](#Rule).
@ -322,26 +322,51 @@ to compare them or compare the model that holds them.
review : List Rule -> Project -> ( List Error, List Rule ) review : List Rule -> Project -> ( List Error, List Rule )
review rules project = review rules project =
let let
( ruleErrors, rulesWithCache ) = sortedModules : Result (Graph.Edge ()) (List (Graph.NodeContext ModuleName ()))
runRules rules project sortedModules =
project
|> Review.Project.moduleGraph
|> Graph.checkAcyclic
|> Result.map Graph.topologicalSort
in in
( List.concat case sortedModules of
[ ruleErrors Ok nodeContexts ->
, project let
|> Review.Project.filesThatFailedToParse ( ruleErrors, rulesWithCache ) =
|> List.map parsingError runRules rules project nodeContexts
] in
, rulesWithCache ( List.concat
) [ ruleErrors
, project
|> Review.Project.filesThatFailedToParse
|> List.map parsingError
]
, rulesWithCache
)
Err _ ->
( [ Error
{ filePath = "GLOBAL ERROR"
, ruleName = "Incorrect project"
, message = "Import cycle discovered"
, details =
[ "I detected an import cycle in your project. This prevents me from working correctly, and results in a error for the Elm compiler anyway. Please resolve it using the compiler's suggestions, then try running `elm-review` again."
]
, range = { start = { row = 0, column = 0 }, end = { row = 0, column = 0 } }
, fixes = Nothing
}
]
, rules
)
runRules : List Rule -> Project -> ( List Error, List Rule ) runRules : List Rule -> Project -> List (Graph.NodeContext ModuleName ()) -> ( List Error, List Rule )
runRules rules project = runRules rules project nodeContexts =
List.foldl List.foldl
(\(Rule _ fn) ( errors, previousRules ) -> (\(Rule _ fn) ( errors, previousRules ) ->
let let
( ruleErrors, ruleWithCache ) = ( ruleErrors, ruleWithCache ) =
fn project fn project nodeContexts
in in
( List.concat [ ruleErrors, errors ], ruleWithCache :: previousRules ) ( List.concat [ ruleErrors, errors ], ruleWithCache :: previousRules )
) )
@ -472,8 +497,8 @@ newModuleRuleCache =
} }
runModuleRule : ModuleRuleSchema { anything | hasAtLeastOneVisitor : () } moduleContext -> ModuleRuleCache moduleContext -> Project -> ( List Error, Rule ) runModuleRule : ModuleRuleSchema { anything | hasAtLeastOneVisitor : () } moduleContext -> ModuleRuleCache moduleContext -> Project -> List (Graph.NodeContext ModuleName ()) -> ( List Error, Rule )
runModuleRule ((ModuleRuleSchema schema) as moduleRuleSchema) previousCache project = runModuleRule ((ModuleRuleSchema schema) as moduleRuleSchema) previousCache project _ =
let let
initialContext : moduleContext initialContext : moduleContext
initialContext = initialContext =
@ -721,125 +746,119 @@ type alias ProjectRuleCache projectContext =
} }
runProjectRule : ProjectRuleSchema projectContext moduleContext -> ProjectRuleCache projectContext -> Project -> ( List Error, Rule ) runProjectRule : ProjectRuleSchema projectContext moduleContext -> ProjectRuleCache projectContext -> Project -> List (Graph.NodeContext ModuleName ()) -> ( List Error, Rule )
runProjectRule ((ProjectRuleSchema schema) as wrappedSchema) startCache project = runProjectRule ((ProjectRuleSchema schema) as wrappedSchema) startCache project nodeContexts =
let let
graph : Graph ModuleName () graph : Graph ModuleName ()
graph = graph =
project project
|> Review.Project.moduleGraph |> Review.Project.moduleGraph
in in
case Graph.checkAcyclic graph |> Result.map Graph.topologicalSort of let
Ok nodeContexts -> initialContext : projectContext
initialContext =
schema.context.initProjectContext
|> accumulateContext schema.elmJsonVisitors (Review.Project.elmJson project)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencyModules project)
modules : Dict ModuleName ProjectModule
modules =
project
|> Review.Project.modules
|> List.foldl
(\module_ dict ->
Dict.insert
(getModuleName module_)
module_
dict
)
Dict.empty
projectModulePaths : Set String
projectModulePaths =
project
|> Review.Project.modules
|> List.map .path
|> Set.fromList
computeModule : ProjectRuleCache projectContext -> List ProjectModule -> ProjectModule -> { source : String, errors : List Error, context : projectContext }
computeModule cache importedModules module_ =
let let
initialContext : projectContext fileKey : FileKey
initialContext = fileKey =
schema.context.initProjectContext FileKey module_.path
|> accumulateContext schema.elmJsonVisitors (Review.Project.elmJson project)
|> accumulateContext schema.dependenciesVisitors (Review.Project.dependencyModules project)
modules : Dict ModuleName ProjectModule moduleNameNode_ : Node ModuleName
modules = moduleNameNode_ =
project moduleNameNode module_.ast.moduleDefinition
|> Review.Project.modules
|> List.foldl
(\module_ dict ->
Dict.insert
(getModuleName module_)
module_
dict
)
Dict.empty
projectModulePaths : Set String initialModuleContext : moduleContext
projectModulePaths = initialModuleContext =
project case schema.traversalType of
|> Review.Project.modules AllModulesInParallel ->
|> List.map .path schema.context.fromProjectToModule
|> Set.fromList fileKey
moduleNameNode_
initialContext
computeModule : ProjectRuleCache projectContext -> List ProjectModule -> ProjectModule -> { source : String, errors : List Error, context : projectContext } ImportedModulesFirst ->
computeModule cache importedModules module_ = importedModules
let |> List.filterMap
fileKey : FileKey (\importedModule ->
fileKey = Dict.get importedModule.path cache
FileKey module_.path |> Maybe.map .context
)
|> List.foldl schema.context.foldProjectContexts initialContext
|> schema.context.fromProjectToModule fileKey moduleNameNode_
moduleNameNode_ : Node ModuleName moduleVisitor : ModuleRuleSchema { hasAtLeastOneVisitor : () } moduleContext
moduleNameNode_ = moduleVisitor =
moduleNameNode module_.ast.moduleDefinition emptySchema "" initialModuleContext
|> schema.moduleVisitorSchema
|> reverseVisitors
initialModuleContext : moduleContext ( fileErrors, context ) =
initialModuleContext = visitModuleForProjectRule
case schema.traversalType of moduleVisitor
AllModulesInParallel -> initialModuleContext
schema.context.fromProjectToModule module_
fileKey
moduleNameNode_
initialContext
ImportedModulesFirst ->
importedModules
|> List.filterMap
(\importedModule ->
Dict.get importedModule.path cache
|> Maybe.map .context
)
|> List.foldl schema.context.foldProjectContexts initialContext
|> schema.context.fromProjectToModule fileKey moduleNameNode_
moduleVisitor : ModuleRuleSchema { hasAtLeastOneVisitor : () } moduleContext
moduleVisitor =
emptySchema "" initialModuleContext
|> schema.moduleVisitorSchema
|> reverseVisitors
( fileErrors, context ) =
visitModuleForProjectRule
moduleVisitor
initialModuleContext
module_
in
{ source = module_.source
, errors = List.map (setFilePathIfUnset module_) fileErrors
, context =
schema.context.fromModuleToProject
fileKey
moduleNameNode_
context
}
newStartCache : ProjectRuleCache projectContext
newStartCache =
startCache
|> Dict.filter (\path _ -> Set.member path projectModulePaths)
newCache : ProjectRuleCache projectContext
newCache =
List.foldl
(computeModuleAndCacheResult schema.traversalType modules graph computeModule)
( newStartCache, Set.empty )
nodeContexts
|> Tuple.first
contextsAndErrorsPerFile : List ( List Error, projectContext )
contextsAndErrorsPerFile =
newCache
|> Dict.values
|> List.map (\cacheEntry -> ( cacheEntry.errors, cacheEntry.context ))
errors : List Error
errors =
List.concat
[ List.concatMap Tuple.first contextsAndErrorsPerFile
, errorsFromFinalEvaluationForProject wrappedSchema initialContext contextsAndErrorsPerFile
]
in in
( errors, Rule schema.name (runProjectRule wrappedSchema newCache) ) { source = module_.source
, errors = List.map (setFilePathIfUnset module_) fileErrors
, context =
schema.context.fromModuleToProject
fileKey
moduleNameNode_
context
}
Err _ -> newStartCache : ProjectRuleCache projectContext
-- TODO return some kind of global error? newStartCache =
( [], Rule schema.name (runProjectRule wrappedSchema startCache) ) startCache
|> Dict.filter (\path _ -> Set.member path projectModulePaths)
newCache : ProjectRuleCache projectContext
newCache =
List.foldl
(computeModuleAndCacheResult schema.traversalType modules graph computeModule)
( newStartCache, Set.empty )
nodeContexts
|> Tuple.first
contextsAndErrorsPerFile : List ( List Error, projectContext )
contextsAndErrorsPerFile =
newCache
|> Dict.values
|> List.map (\cacheEntry -> ( cacheEntry.errors, cacheEntry.context ))
errors : List Error
errors =
List.concat
[ List.concatMap Tuple.first contextsAndErrorsPerFile
, errorsFromFinalEvaluationForProject wrappedSchema initialContext contextsAndErrorsPerFile
]
in
( errors, Rule schema.name (runProjectRule wrappedSchema newCache) )
setRuleName : String -> Error -> Error setRuleName : String -> Error -> Error