From f6f5326f3d62a17b8d491cc30b63f300bafad8eb Mon Sep 17 00:00:00 2001 From: Jeroen Engels Date: Thu, 27 Aug 2020 08:21:53 +0200 Subject: [PATCH] Expose ModuleNameLookupTable --- docs.json | 2 +- elm.json | 1 + src/Review/ModuleNameLookupTable.elm | 90 ++++++++++++++++++++++++++-- src/Review/Rule.elm | 11 ++-- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/docs.json b/docs.json index a8626952..eeda2971 100644 --- a/docs.json +++ b/docs.json @@ -1 +1 @@ -[{"name":"Review.Fix","comment":" Tools to write automatic error fixes.\n\nWhen creating a [`Review.Rule.Error`](./Review-Rule#Error), you can provide an automatic\nfix for the error using [`Review.Rule.errorWithFix`](./Review-Rule#errorWithFix)\nor other functions that end with \"withFix\" so that the user doesn't need to fix\nthe problem themselves.\n\nIn the [CLI], the user can ask to fix the errors automatically, and in doing so,\nthey will be presented by a fix which they can accept or refuse. If the fix gets\nrefused, then the next fixable error will be presented. Otherwise, if the fix\ngets accepted, the file will be applied and the fixed file content get analyzed\nagain by the different rules in the user's configuration, and then another fix\nwill be presented. When there are no more fixable errors, the remaining errors\nwill be reported, just like when the user doesn't request errors to be automatically\nfixed.\n\nIn summary, errors will be presented one by one and the user will validate them.\nThe [CLI] also proposes an option to fix all the errors, which applies each fix\none by one and then asks the user to confirm the cumulated fix.\n\n\n# Guidelines\n\nAn automatic fix, when applied, should resolve the reported error completely.\nThis means that when the automatic fix is applied, the user should not have to\nthink about the error anymore or have to do additional work.\n\nImagine if the user applies a lot of automatic fixes all at once. We don't want them to have to\nremember having to do something, otherwise we may have just offloaded a lot of\nwork that they may forget to do. In that case, it is better not to provide a fix\nat all, so that they keep a reminder and the details of how to fix the problem.\n\nAn automatic fix should resolve only the reported error, not try to fix other\npotential errors. By only fixing one error at a time, the fix will be easier for\nthe user to digest and understand. The file will be re-reviewed when the fix is\napplied, and then another error can fix that one.\n\n\n# When (not) to provide an automatic fix?\n\nFor users, having an automatic fix always feels like a nice-to-have and they may\nrequest you to provide some, but they are not mandatory, and in some cases, it\nis better not to have any.\n\n\n## Reasons not to provide an automatic fix\n\n\n### A complete automatic fix is not possible\n\nSometimes, just by going through the whole file, you are missing some of the\ninformation needed to generate the right fix. Instead of providing a partial or\npotentially incorrect fix, it would be better to provide more details, hints or\nsuggestions.\n\n\n### The fix would result in a compiler error\n\nAn automatic fix should not cause changes that would break the file or the\nproject. In some cases, we can detect that the [fix will break things](#Problem),\nlike if the result of the fix is invalid Elm code (as in resulting in a parsing\nerror), but ultimately we can't detect that the project will still compile after\nthe fix is applied.\n\nUsers are notified that an automatic fix is available. For performance reasons,\nwe only check that a fix is valid before presenting it to the user and ignore it\nif it turns out to be invalid. This means that the user will be disappointed or\nconfused when the error ends up not being enforced. The only way we have to\nprevent this is to write tests, as fixes are applied in tests.\n\n\n### The user should learn about problem and how to solve it\n\nSometimes problems are learning opportunities, and it is worth having the user\nspend some time reading through the details of the error and trying several\nalternatives in order to understand the problem and the tradeoffs of the\nsolutions. You can guide them by using great error details!\n\n\n## Reasons to provide an automatic fix\n\nThe reasons to provide an automatic fix are basically the opposite of the\nreasons not to provide an automatic fix:\n\n - We know how to fix the problem completely and accurately\n - The task is menial and the user will not learn anything by fixing the error\n themselves\n\n\n# Strategies for writing automatic fixes effectively\n\n\n### Write a lot of tests\n\nAutomatic fixes are more error-prone than rules, especially since we may work\nwith re-writing ports of the code, for which the AST does not provide the\ncurrent formatting of a file (there is no information about spacing,\nline-breaks, ...). I suggest writing a lot of tests, and especially write tests\nwhere the formatting of the original file is a bit odd, as you may for instance\nunknowingly attempt to delete characters next to the thing you wanted to remove.\n\n\n### Store ranges in the context if necessary\n\nFixes work with ranges or position. If the context of a different element is not\navailable in the scope of where you create the error, then you should store it\nin the context of your rule.\n\n\n# Creating a fix\n\n@docs Fix, removeRange, replaceRangeBy, insertAt\n\n\n# Applying fixes\n\n@docs FixResult, Problem, fix\n\n[CLI]: https://github.com/jfmengels/node-elm-review\n\n\n# Tooling\n\n@docs toRecord\n\n","unions":[{"name":"FixResult","comment":" Represents the result of having applied a list of fixes to a source code.\n","args":[],"cases":[["Successful",["String.String"]],["Errored",["Review.Fix.Problem"]]]},{"name":"Problem","comment":" Represents a problem that may have occurred when attempting to apply a list\nof fixes.\n","args":[],"cases":[["Unchanged",[]],["SourceCodeIsNotValid",["String.String"]],["HasCollisionsInFixRanges",[]]]}],"aliases":[{"name":"Fix","comment":" Represents (part of a) fix that will be applied to a file's source code in order to\nautomatically fix a review error.\n","args":[],"type":"Review.Error.Fix"}],"values":[{"name":"fix","comment":" Apply the changes on the source code.\n","type":"Review.Error.Target -> List.List Review.Fix.Fix -> String.String -> Review.Fix.FixResult"},{"name":"insertAt","comment":" Insert some code at the given position.\n","type":"{ row : Basics.Int, column : Basics.Int } -> String.String -> Review.Fix.Fix"},{"name":"removeRange","comment":" Remove the code in between a range.\n","type":"Elm.Syntax.Range.Range -> Review.Fix.Fix"},{"name":"replaceRangeBy","comment":" Replace the code in between a range by some other code.\n","type":"Elm.Syntax.Range.Range -> String.String -> Review.Fix.Fix"},{"name":"toRecord","comment":" Describe a fix as a range to replace by something else.\n\nThis is meant for external tooling. You shouldn't have to care about this\n\n","type":"Review.Fix.Fix -> { range : Elm.Syntax.Range.Range, replacement : String.String }"}],"binops":[]},{"name":"Review.Project","comment":" Represents the contents of the project to be analyzed. This information will\nthen be fed to the review rules.\n\nYou may need to use use this module if you want\n\n - to create test cases where the project is in a certain configuration\n - to make `elm-review` run in a new environment\n\nYou can safely ignore this module if you just want to write a review rule that\ndoes not look at project information (like the `elm.json`, dependencies, ...).\n\n@docs Project, new\n\n\n## Elm modules\n\n@docs ProjectModule, addModule, addParsedModule, removeModule, modules, modulesThatFailedToParse, precomputeModuleGraph\n\n\n# `elm.json`\n\n@docs addElmJson, elmJson\n\n\n# `README.md`\n\n@docs addReadme, readme\n\n\n# Project dependencies\n\n@docs addDependency, removeDependencies, dependencies\n\n","unions":[],"aliases":[{"name":"Project","comment":" Holds all the information related to the project such as the contents of\nthe `elm.json` file, the project modules and the project dependencies.\n","args":[],"type":"Review.Project.Internal.Project"},{"name":"ProjectModule","comment":" Represents a parsed file.\n","args":[],"type":"Review.Project.Internal.ProjectModule"}],"values":[{"name":"addDependency","comment":" Add a dependency to the project. These will be available for rules to make\nbetter assumptions on what is happening in the code.\n\nKnowing the dependencies of the project will also help better parse the source\nfiles, since the dependencies will allow us to know the precedence and\nassociativity of operators, which has an impact on the resulting AST when\nparsing a file.\n\n","type":"Review.Project.Dependency.Dependency -> Review.Project.Project -> Review.Project.Project"},{"name":"addElmJson","comment":" Add the content of the `elm.json` file to the project, making it\navailable for rules to access using\n[`Review.Rule.withElmJsonModuleVisitor`](./Review-Rule#withElmJsonModuleVisitor) and\n[`Review.Rule.withElmJsonProjectVisitor`](./Review-Rule#withElmJsonProjectVisitor).\n\nThe `raw` value should be the raw JSON as a string, and `project` corresponds to\n[`elm/project-metadata-utils`'s `Elm.Project.Project` type](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project#Project).\n\n","type":"{ path : String.String, raw : String.String, project : Elm.Project.Project } -> Review.Project.Project -> Review.Project.Project"},{"name":"addModule","comment":" Add an Elm file to the project. If a file with the same path already exists,\nthen it will replace it.\n\nIf the file is syntactically valid Elm code, it will then be analyzed by the\nreview rules. Otherwise, the file will be added to the list of files that failed\nto parse, which you can get using [`modulesThatFailedToParse`](#modulesThatFailedToParse),\nand for which a parsing error will be reported when running [`Review.review`](./Review#review).\n\n","type":"{ path : String.String, source : String.String } -> Review.Project.Project -> Review.Project.Project"},{"name":"addParsedModule","comment":" Add an already parsed module to the project. This module will then be analyzed by the rules.\n","type":"{ path : String.String, source : String.String, ast : Elm.Syntax.File.File } -> Review.Project.Project -> Review.Project.Project"},{"name":"addReadme","comment":" Add the content of the `README.md` file to the project, making it\navailable for rules to access using\n[`Review.Rule.withReadmeModuleVisitor`](./Review-Rule#withReadmeModuleVisitor) and\n[`Review.Rule.withReadmeProjectVisitor`](./Review-Rule#withReadmeProjectVisitor).\n","type":"{ path : String.String, content : String.String } -> Review.Project.Project -> Review.Project.Project"},{"name":"dependencies","comment":" Get the [dependencies](./Review-Project-Dependency#Dependency) of the project.\n","type":"Review.Project.Project -> Dict.Dict String.String Review.Project.Dependency.Dependency"},{"name":"elmJson","comment":" Get the contents of the `elm.json` file, if available.\n\nThis will give you a `Elm.Project.Project` type from the\n[`elm/project-metadata-utils`](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.0/Elm-Project)\npackage, so you will need to install and use it to gain access to the\ninformation from the `elm.json` file.\n\n","type":"Review.Project.Project -> Maybe.Maybe { path : String.String, raw : String.String, project : Elm.Project.Project }"},{"name":"modules","comment":" Get the list of modules in the project.\n","type":"Review.Project.Project -> List.List Review.Project.ProjectModule"},{"name":"modulesThatFailedToParse","comment":" Get the list of file paths that failed to parse, because they were syntactically invalid Elm code.\n","type":"Review.Project.Project -> List.List { path : String.String, source : String.String }"},{"name":"new","comment":" Create a new Project.\n","type":"Review.Project.Project"},{"name":"precomputeModuleGraph","comment":" Precomputes the module graph that you get using [`moduleGraph`](#moduleGraph).\n\nThis is to avoid a potentially long computation at every review run. Once the graph\nis precomputed, it will be recomputed every time a module is changed, meaning\nyou won't need to reuse this call `precomputeModuleGraph` again.\n\nYou should use this function if and when you know you loaded all the files in\nthe project.\n\n","type":"Review.Project.Project -> Review.Project.Project"},{"name":"readme","comment":" Get the contents of the `README.md` file, if available.\n","type":"Review.Project.Project -> Maybe.Maybe { path : String.String, content : String.String }"},{"name":"removeDependencies","comment":" Remove all dependencies of a project. Use this to flush the dependencies of\na project when they are changed, before re-adding them.\n","type":"Review.Project.Project -> Review.Project.Project"},{"name":"removeModule","comment":" Remove a module from the project by its path.\n","type":"String.String -> Review.Project.Project -> Review.Project.Project"}],"binops":[]},{"name":"Review.Project.Dependency","comment":" Functions to create and read data about the project's dependencies.\n\nYou may need to use use this module if you want\n\n - to create test cases where the project has specific dependencies\n - to make `elm-review` run in a new environment\n\nYou can safely ignore this module if you just want to write a review rule that\ndoes not look at the project's dependencies.\n\n@docs Dependency\n\n\n# Access\n\n@docs name, elmJson, modules\n\n\n# Create\n\n@docs create\n\n","unions":[{"name":"Dependency","comment":" Represents a dependency of the project.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"create","comment":" Create a dependency.\n\nYou will need to use this if you try to make `elm-review` run in a new environment,\nbut you can safely ignore it if you just want to write a review rule or run it\nin existing environments like the CLI tool.\n\nYou could something like the following to create a test project in your tests.\n\n import Elm.Docs\n import Elm.Project\n import Elm.Type as Type\n import Json.Decode as Decode\n import Review.Project as Project exposing (Project)\n import Review.Project.Dependency as Dependency exposing (Dependency)\n\n testProject : Project\n testProject =\n Project.new\n |> Project.addDependency\n\n dependency : String -> Dependency\n dependency license =\n Dependency.create\n \"author/dependency\"\n (createElmJson rawElmJson)\n modules\n\n modules : List Elm.Docs.Module\n modules =\n [ { name = \"Foo\"\n , comment = \"\"\n , unions = []\n , aliases = []\n , values =\n [ { name = \"someFunction\"\n , comment = \"\"\n , tipe = Type.Lambda (Type.Var \"a\") (Type.Var \"b\")\n }\n ]\n , binops = []\n }\n ]\n\n createElmJson : String -> Elm.Project.Project\n createElmJson rawElmJson =\n case Decode.decodeString Elm.Project.decoder rawElmJson of\n Ok elmJson ->\n elmJson\n\n Err _ ->\n Debug.todo \"Invalid elm.json contents in tests\"\n\n rawElmJson : String\n rawElmJson =\n \"\"\"\n {\n \"type\": \"package\",\n \"name\": \"author/dependency\",\n \"summary\": \"Summary\",\n \"license\": \"MIT\",\n \"version\": \"1.0.0\",\n \"exposed-modules\": [\n \"Foo\"\n ],\n \"elm-version\": \"0.19.0 <= v < 0.20.0\",\n \"dependencies\": {\n \"elm/core\": \"1.0.0 <= v < 2.0.0\"\n },\n \"test-dependencies\": {}\n }\n \"\"\"\n\n","type":"String.String -> Elm.Project.Project -> List.List Elm.Docs.Module -> Review.Project.Dependency.Dependency"},{"name":"elmJson","comment":" Get the [contents of the project's `elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.1/Elm-Project#Project)\nfile.\n","type":"Review.Project.Dependency.Dependency -> Elm.Project.Project"},{"name":"modules","comment":" Get the list of [modules](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.1/Elm-Docs#Module)\ncontained in the dependency.\n","type":"Review.Project.Dependency.Dependency -> List.List Elm.Docs.Module"},{"name":"name","comment":" Get the name of the dependency.\n","type":"Review.Project.Dependency.Dependency -> String.String"}],"binops":[]},{"name":"Review.Rule","comment":" This module contains functions that are used for writing rules.\n\n\n# How does it work?\n\n`elm-review` reads the modules, `elm.json`, dependencies and `README.md` from your project,\nand turns each module into an [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree),\na tree-like structure which represents your source code, using the\n[`elm-syntax` package](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/).\n\n`elm-review` then feeds all this data into `review rules`, that traverse them to report problems.\nThe way that review rules go through the data depends on whether it is a [module rule](#creating-a-module-rule) or a [project rule](#creating-a-project-rule).\n\n`elm-review` relies on the [`elm-syntax` package](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/),\nand all the node types you'll see will be coming from there. You are likely to\nneed to have the documentation for that package open when writing a rule.\n\nThere are plenty of examples in this documentation, and you can also look at the\nsource code of existing rules to better grasp how they work.\n\n**NOTE**: These examples are only here to showcase how to write rules and how a function can\nbe used. They are not necessarily good rules to enforce. See the\n[section on whether to write a rule](./#when-to-write-or-enable-a-rule) for more on that.\nEven if you think they are good ideas to enforce, they are often not complete, as there are other\npatterns you would want to forbid, but that are not handled by the example.\n\n\n# What makes a good rule\n\nApart from the rationale on [whether a rule should be written](./#when-to-write-or-enable-a-rule),\nhere are a few tips on what makes a rule helpful.\n\nA review rule is an automated communication tool which sends messages to\ndevelopers who have written patterns your rule wishes to prevent. As all\ncommunication, the message is important.\n\n\n## A good rule name\n\nThe name of the rule (`NoUnusedVariables`, `NoDebug`, ...) should try to convey\nreally quickly what kind of pattern we're dealing with. Ideally, a user who\nencounters this pattern for the first time could guess the problem just from the\nname. And a user who encountered it several times should know how to fix the\nproblem just from the name too.\n\nI recommend having the name of the module containing the rule be the same as the\nrule name. This will make it easier to find the module in the project or on\nthe packages website when trying to get more information.\n\n\n## A helpful error message and details\n\nThe error message should give more information about the problem. It is split\ninto two parts:\n\n - The `message`: A short sentence that describes the forbidden pattern. A user\n that has encountered this error multiple times should know exactly what to do.\n Example: \"Function `foo` is never used\". With this information, a user who\n knows the rule probably knows that a function needs to be removed from the\n source code, and also knows which one.\n - The `details`: All the additional information that can be useful to the\n user, such as the rationale behind forbidding the pattern, and suggestions\n for a solution or alternative.\n\nWhen writing the error message that the user will see, try to make them be as\nhelpful as the messages the compiler gives you when it encounters a problem.\n\n\n## The smallest section of code that makes sense\n\nWhen creating an error, you need to specify under which section of the code this\nmessage appears. This is where you would see squiggly lines in your editor when\nyou have review or compiler errors.\n\nTo make the error easier to spot, it is best to make this section as small as\npossible, as long as that makes sense. For instance, in a rule that would forbid\n`Debug.log`, you would the error to appear under `Debug.log`, not on the whole\nfunction which contains this piece of code.\n\n\n## Good rule documentation\n\nThe rule documentation should give the same information as what you would see in\nthe error message.\n\nIf published in a package, the rule documentation should explain when not to\nenable the rule in the user's review configuration. For instance, for a rule that\nmakes sure that a package is publishable by ensuring that all docs are valid,\nthe rule might say something along the lines of \"If you are writing an\napplication, then you should not use this rule\".\n\nAdditionally, it could give a few examples of patterns that will be reported and\nof patterns that will not be reported, so that users can have a better grasp of\nwhat to expect.\n\n\n# Strategies for writing rules effectively\n\n\n## Use Test-Driven Development\n\nThis package comes with [`Review.Test`](./Review-Test), which works with [`elm-test`](https://github.com/elm-explorations/test).\nI recommend reading through [`the strategies for effective testing`](./Review-Test#strategies-for-effective-testing) before\nstarting writing a rule.\n\n\n## Look at the documentation for [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/)\n\n`elm-review` is heavily dependent on the types that [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/)\nprovides. If you don't understand the AST it provides, you will have a hard time\nimplementing the rule you wish to create.\n\n\n# Writing a Rule\n\n@docs Rule\n\n\n## Creating a module rule\n\nA \"module rule\" looks at modules (i.e. files) one by one. When it finishes looking at a module and reporting errors,\nit forgets everything about the module it just analyzed before starting to look at a different module. You should create one of these if you\ndo not need to know the contents of a different module in the project, such as what functions are exposed.\nIf you do need that information, you should create a [project rule](#creating-a-project-rule).\n\nIf you are new to writing rules, I would recommend learning how to build a module rule first, as they are in practice a\nsimpler version of project rules.\n\nThe traversal of a module rule is the following:\n\n - Read project-related info (only collect data in the context in these steps)\n - The `elm.json` file, visited by [`withElmJsonModuleVisitor`](#withElmJsonModuleVisitor)\n - The `README.md` file, visited by [`withReadmeModuleVisitor`](#withReadmeModuleVisitor)\n - The definition for dependencies, visited by [`withDependenciesModuleVisitor`](#withDependenciesModuleVisitor)\n - Visit the Elm module (in the following order)\n - The module definition, visited by [`withSimpleModuleDefinitionVisitor`](#withSimpleModuleDefinitionVisitor) and [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor)\n - The module's list of comments, visited by [`withSimpleCommentsVisitor`](#withSimpleCommentsVisitor) and [`withCommentsVisitor`](#withCommentsVisitor)\n - Each import, visited by [`withSimpleImportVisitor`](#withSimpleImportVisitor) and [`withImportVisitor`](#withImportVisitor)\n - The list of declarations, visited by [`withDeclarationListVisitor`](#withDeclarationListVisitor)\n - Each declaration, visited by [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor), [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor).\n Before evaluating the next declaration, the expression contained in the declaration\n will be visited recursively by [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor), [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor)\n - A final evaluation is made when the module has fully been visited, using [`withFinalModuleEvaluation`](#withFinalModuleEvaluation)\n\nEvaluating/visiting a node means two things:\n\n - Detecting patterns and reporting errors\n - Collecting data in a \"context\" (called `moduleContext` for module rules) to have more information available in a later\n node evaluation. You can only use the context and update it with \"non-simple with\\*\" visitor functions.\n I recommend using the \"simple with\\*\" visitor functions if you don't need to do either, as they are simpler to use\n\n@docs ModuleRuleSchema, newModuleRuleSchema, fromModuleRuleSchema\n\n\n## Builder functions without context\n\n@docs withSimpleModuleDefinitionVisitor, withSimpleCommentsVisitor, withSimpleImportVisitor, withSimpleDeclarationVisitor, withSimpleExpressionVisitor\n\n\n## Builder functions with context\n\n@docs newModuleRuleSchemaUsingContextCreator\n@docs withModuleDefinitionVisitor\n@docs withCommentsVisitor\n@docs withImportVisitor\n@docs Direction, withDeclarationEnterVisitor, withDeclarationExitVisitor, withDeclarationVisitor, withDeclarationListVisitor\n@docs withExpressionEnterVisitor, withExpressionExitVisitor, withExpressionVisitor\n@docs withFinalModuleEvaluation\n\n\n## Builder functions to analyze the project's data\n\n@docs withElmJsonModuleVisitor, withReadmeModuleVisitor, withDependenciesModuleVisitor\n\n\n## Creating a project rule\n\nProject rules can look at the global picture of an Elm project. Contrary to module\nrules, which forget everything about the module they were looking at when going from\none module to another, project rules can retain information about previously\nanalyzed modules, and use it to report errors when analyzing a different module or\nafter all modules have been visited.\n\nProject rules can also report errors in the `elm.json` or the `README.md` files.\n\nIf you are new to writing rules, I would recommend learning [how to build a module rule](#creating-a-module-rule)\nfirst, as they are in practice a simpler version of project rules.\n\n@docs ProjectRuleSchema, newProjectRuleSchema, fromProjectRuleSchema, withModuleVisitor, withModuleContext, withModuleContextUsingContextCreator, withElmJsonProjectVisitor, withReadmeProjectVisitor, withDependenciesProjectVisitor, withFinalProjectEvaluation, withContextFromImportedModules\n\n\n## Requesting more information\n\n@docs ContextCreator, Metadata, initContextCreator, isInSourceDirectories, moduleNameFromMetadata, moduleNameNodeFromMetadata, withMetadata, withModuleNameLookupTable, withModuleKey\n\n\n## Errors\n\n@docs Error, error, errorWithFix, ModuleKey, errorForModule, errorForModuleWithFix, ElmJsonKey, errorForElmJson, ReadmeKey, errorForReadme, errorForReadmeWithFix\n@docs ReviewError, errorRuleName, errorMessage, errorDetails, errorRange, errorFixes, errorFilePath, errorTarget\n\n\n## Configuring exceptions\n\nThere are situations where you don't want review rules to report errors:\n\n1. You copied and updated over an external library because one of your needs wasn't met, and you don't want to modify it more than necessary.\n2. Your project contains generated source code, over which you have no control or for which you do not care that some rules are enforced (like the reports of unused variables).\n3. You want to introduce a rule progressively, because there are too many errors in the project for you to fix in one go. You can then ignore the parts of the project where the problem has not yet been solved, and fix them as you go.\n4. You wish to disable some rules for tests files (or enable some only for tests).\n\nYou can use the following functions to ignore errors in directories or files.\n\n**NOTE**: Even though they can be used to disable any errors, I **strongly recommend against**\ndoing so if you are not in the situations listed above. I highly recommend you\nleave a comment explaining the reason why you use these functions, or to\ncommunicate with your colleagues if you see them adding exceptions without\nreason or seemingly inappropriately.\n\n@docs ignoreErrorsForDirectories, ignoreErrorsForFiles\n\n\n# Running rules\n\n@docs review, reviewWithPrecollectionOfData, ruleName\n\n\n# Internals\n\n@docs Required, Forbidden\n\n","unions":[{"name":"ContextCreator","comment":" Create a context based on some other context.\nUse functions like [`withMetadata`](#withMetadata) to request more information.\n\n`from` will usually be a `projectContext` and `to` a `moduleContext`.\n\n","args":["from","to"],"cases":[]},{"name":"Direction","comment":" **DEPRECATED**\n\nThis is used in [`withDeclarationVisitor`](#withDeclarationVisitor) and [`withDeclarationVisitor`](#withDeclarationVisitor),\nwhich are deprecated and will be removed in the next major version. This type will be removed along with them.\n\nTo replicate the same behavior, take a look at\n\n - [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor).\n - [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor).\n\n**/DEPRECATED**\n\nRepresents whether a node is being traversed before having seen its children (`OnEnter`ing the node), or after (`OnExit`ing the node).\n\nWhen visiting the AST, declaration and expression nodes are visited twice: once\nwith `OnEnter`, before the children of the node are visited, and once with\n`OnExit`, after the children of the node have been visited.\nIn most cases, you'll only want to handle the `OnEnter` case, but there are cases\nwhere you'll want to visit a [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node)\nafter having seen its children.\n\nFor instance, if you are trying to detect the unused variables defined inside\nof a let expression, you will want to collect the declaration of variables,\nnote which ones are used, and at the end of the block report the ones that weren't used.\n\n expressionVisitor : Node Expression -> Direction -> Context -> ( List (Error {}), Context )\n expressionVisitor node direction context =\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Expression.FunctionOrValue moduleName name ) ->\n ( [], markVariableAsUsed context name )\n\n -- Find variables declared in let expression\n ( Rule.OnEnter, Expression.LetExpression letBlock ) ->\n ( [], registerVariables context letBlock )\n\n -- When exiting the let expression, report the variables that were not used.\n ( Rule.OnExit, Expression.LetExpression _ ) ->\n ( unusedVariables context |> List.map createError, context )\n\n _ ->\n ( [], context )\n\n","args":[],"cases":[["OnEnter",[]],["OnExit",[]]]},{"name":"ElmJsonKey","comment":" A key to be able to report an error for the `elm.json` file. You need this\nkey in order to use the [`errorForElmJson`](#errorForElmJson) function. This is\nto prevent creating errors for it if you have not visited it.\n\nYou can get a `ElmJsonKey` using the [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor) function.\n\n","args":[],"cases":[]},{"name":"Error","comment":" Represents an error found by a [`Rule`](#Rule). These are created by the\nrules.\n","args":["scope"],"cases":[]},{"name":"Forbidden","comment":" Used for phantom type constraints. You can safely ignore this type.\n","args":[],"cases":[]},{"name":"Metadata","comment":" Metadata for the module being visited.\n\nDo not store the metadata directly in your context. Prefer storing the indiviaual pieces of informa\n\n","args":[],"cases":[]},{"name":"ModuleKey","comment":" A key to be able to report an error for a specific module. You need such a\nkey in order to use the [`errorForModule`](#errorForModule) function. This is to\nprevent creating errors for modules you have not visited, or files that do not exist.\n\nYou can get a `ModuleKey` from the `fromProjectToModule` and `fromModuleToProject`\nfunctions that you define when using [`newProjectRuleSchema`](#newProjectRuleSchema).\n\n","args":[],"cases":[]},{"name":"ModuleRuleSchema","comment":" Represents a schema for a module [`Rule`](#Rule).\n\nStart by using [`newModuleRuleSchema`](#newModuleRuleSchema), then add visitors to look at the parts of the code you are interested in.\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebug\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n","args":["schemaState","moduleContext"],"cases":[]},{"name":"ProjectRuleSchema","comment":" Represents a schema for a project [`Rule`](#Rule).\n\nSee the documentation for [`newProjectRuleSchema`](#newProjectRuleSchema) for\nhow to create a project rule.\n\n","args":["schemaState","projectContext","moduleContext"],"cases":[]},{"name":"ReadmeKey","comment":" A key to be able to report an error for the `README.md` file. You need this\nkey in order to use the [`errorForReadme`](#errorForReadme) function. This is\nto prevent creating errors for it if you have not visited it.\n\nYou can get a `ReadmeKey` using the [`withReadmeProjectVisitor`](#withReadmeProjectVisitor) function.\n\n","args":[],"cases":[]},{"name":"Required","comment":" Used for phantom type constraints. You can safely ignore this type.\n","args":[],"cases":[]},{"name":"Rule","comment":" Represents a construct able to analyze a project and report\nunwanted patterns.\n\nYou can create [module rules](#creating-a-module-rule) or [project rules](#creating-a-project-rule).\n\n","args":[],"cases":[]}],"aliases":[{"name":"ReviewError","comment":" Represents an error found by a [`Rule`](#Rule). These are the ones that will\nbe reported to the user.\n\nIf you are building a [`Rule`](#Rule), you shouldn't have to use this.\n\n","args":[],"type":"Review.Error.ReviewError"}],"values":[{"name":"error","comment":" Create an [`Error`](#Error). Use it when you find a pattern that the rule should forbid.\n\nThe `message` and `details` represent the [message you want to display to the user].\nThe `details` is a list of paragraphs, and each item will be visually separated\nwhen shown to the user. The details may not be empty, and this will be enforced\nby the tests automatically.\n\n error : Node a -> Error\n error node =\n Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n\nThe [`Range`] corresponds to the location where the error should be shown, i.e. where to put the squiggly lines in an editor.\nIn most cases, you can get it using [`Node.range`].\n\n[message you want to display to the user]: #a-helpful-error-message-and-details\n\n[`Range`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range\n[`Node.range`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#range\n\n","type":"{ message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error {}"},{"name":"errorDetails","comment":" Get the error details of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> List.List String.String"},{"name":"errorFilePath","comment":" Get the file path of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorFixes","comment":" Get the automatic [`fixes`](./Review-Fix#Fix) of an [`Error`](#Error), if it\ndefined any.\n","type":"Review.Error.ReviewError -> Maybe.Maybe (List.List Review.Fix.Fix)"},{"name":"errorForElmJson","comment":" Create an [`Error`](#Error) for the `elm.json` file.\n\nYou will need an [`ElmJsonKey`](#ElmJsonKey), which you can get from the [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)\nfunction.\n\nThe second argument is a function that takes the `elm.json` content as a raw string,\nand returns the error details. Using the raw string, you should try and find the\nmost fitting [`Range`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range)\npossible for the error.\n\n","type":"Review.Rule.ElmJsonKey -> (String.String -> { message : String.String, details : List.List String.String, range : Elm.Syntax.Range.Range }) -> Review.Rule.Error scope"},{"name":"errorForModule","comment":" Just like [`error`](#error), create an [`Error`](#Error) but for a specific module, instead of the module that is being\nvisited.\n\nYou will need a [`ModuleKey`](#ModuleKey), which you can get from the `fromProjectToModule` and `fromModuleToProject`\nfunctions that you define when using [`newProjectRuleSchema`](#newProjectRuleSchema).\n\n","type":"Review.Rule.ModuleKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error scope"},{"name":"errorForModuleWithFix","comment":" Just like [`errorForModule`](#errorForModule), create an [`Error`](#Error) for a specific module, but\nprovides an automatic fix that the user can apply.\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`errorForModule`](#errorForModule) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"Review.Rule.ModuleKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error scope"},{"name":"errorForReadme","comment":" Create an [`Error`](#Error) for the `README.md` file.\n\nYou will need an [`ReadmeKey`](#ReadmeKey), which you can get from the [`withReadmeProjectVisitor`](#withReadmeProjectVisitor)\nfunction.\n\n","type":"Review.Rule.ReadmeKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error scope"},{"name":"errorForReadmeWithFix","comment":" Just like [`errorForReadme`](#errorForReadme), create an [`Error`](#Error) for the `README.md` file, but\nprovides an automatic fix that the user can apply.\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`errorForReadme`](#errorForReadme) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"Review.Rule.ReadmeKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error scope"},{"name":"errorMessage","comment":" Get the error message of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorRange","comment":" Get the [`Range`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range)\nof an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> Elm.Syntax.Range.Range"},{"name":"errorRuleName","comment":" Get the name of the rule that triggered this [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorTarget","comment":" Get the file path of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> Review.Error.Target"},{"name":"errorWithFix","comment":" Creates an [`Error`](#Error), just like the [`error`](#error) function, but\nprovides an automatic fix that the user can apply.\n\n import Review.Fix as Fix\n\n error : Node a -> Error\n error node =\n Rule.errorWithFix\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n [ Fix.removeRange (Node.range node) ]\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`error`](#error) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"{ message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error {}"},{"name":"fromModuleRuleSchema","comment":" Create a [`Rule`](#Rule) from a configured [`ModuleRuleSchema`](#ModuleRuleSchema).\n","type":"Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext -> Review.Rule.Rule"},{"name":"fromProjectRuleSchema","comment":" Create a [`Rule`](#Rule) from a configured [`ProjectRuleSchema`](#ProjectRuleSchema).\n","type":"Review.Rule.ProjectRuleSchema { schemaState | withModuleContext : Review.Rule.Forbidden, hasAtLeastOneVisitor : () } projectContext moduleContext -> Review.Rule.Rule"},{"name":"ignoreErrorsForDirectories","comment":" Ignore the errors reported for modules in specific directories of the project.\n\nUse it when you don't want to get review errors for generated source code or for\nlibraries that you forked and copied over to your project.\n\n config : List Rule\n config =\n [ Some.Rule.rule\n |> Rule.ignoreErrorsForDirectories [ \"generated-source/\", \"vendor/\" ]\n , Some.Other.Rule.rule\n ]\n\nIf you want to ignore some directories for all of your rules, you can apply\n`ignoreErrorsForDirectories` like this:\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n |> List.map (Rule.ignoreErrorsForDirectories [ \"generated-source/\", \"vendor/\" ])\n\nThe paths should be relative to the `elm.json` file, just like the ones for the\n`elm.json`'s `source-directories`.\n\nYou can apply `ignoreErrorsForDirectories`several times for a rule, to add more\nignored directories.\n\nYou can also use it when writing a rule. We can hardcode in the rule that a rule\nis not applicable to a folder, like `tests/` for instance. The following example\nforbids using `Debug.todo` anywhere in the code, except in tests.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForDirectories [ \"tests/\" ]\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node =\n case Node.value node of\n Expression.FunctionOrValue [ \"Debug\" ] \"todo\" ->\n [ Rule.error\n { message = \"Remove the use of `Debug.todo` before shipping to production\"\n , details = [ \"`Debug.todo` is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\n","type":"List.List String.String -> Review.Rule.Rule -> Review.Rule.Rule"},{"name":"ignoreErrorsForFiles","comment":" Ignore the errors reported for specific file paths.\nUse it when you don't want to review generated source code or files from external\nsources that you copied over to your project and don't want to be touched.\n\n config : List Rule\n config =\n [ Some.Rule.rule\n |> Rule.ignoreErrorsForFiles [ \"src/Some/File.elm\" ]\n , Some.Other.Rule.rule\n ]\n\nIf you want to ignore some files for all of your rules, you can apply\n`ignoreErrorsForFiles` like this:\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n |> List.map (Rule.ignoreErrorsForFiles [ \"src/Some/File.elm\" ])\n\nThe paths should be relative to the `elm.json` file, just like the ones for the\n`elm.json`'s `source-directories`.\n\nYou can apply `ignoreErrorsForFiles` several times for a rule, to add more\nignored files.\n\nYou can also use it when writing a rule. We can simplify the example from [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor)\nby hardcoding an exception into the rule (that forbids the use of `Html.button` except in the \"Button\" module).\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoHtmlButton\"\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForFiles [ \"src/Button.elm\" ]\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue [ \"Html\" ] \"button\" ->\n [ Rule.error\n { message = \"Do not use `Html.button` directly\"\n , details = [ \"At fruits.com, we've built a nice `Button` module that suits our needs better. Using this module instead of `Html.button` ensures we have a consistent button experience across the website.\" ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\n","type":"List.List String.String -> Review.Rule.Rule -> Review.Rule.Rule"},{"name":"initContextCreator","comment":" Initialize a new context creator.\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { moduleName = Rule.moduleNameFromMetadata metadata\n\n -- ...other fields\n }\n )\n |> Rule.withMetadata\n\n","type":"(from -> to) -> Review.Rule.ContextCreator from to"},{"name":"isInSourceDirectories","comment":" Learn whether the current module is in the \"source-directories\" of the project. You can use this information to\nknow whether the module is part of the tests or of the production code.\n","type":"Review.Rule.Metadata -> Basics.Bool"},{"name":"moduleNameFromMetadata","comment":" Get the module name of the current module.\n","type":"Review.Rule.Metadata -> Elm.Syntax.ModuleName.ModuleName"},{"name":"moduleNameNodeFromMetadata","comment":" Get the [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node) to the module name of the current module.\n","type":"Review.Rule.Metadata -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName"},{"name":"newModuleRuleSchema","comment":" Creates a schema for a module rule. Will require adding module visitors\ncalling [`fromModuleRuleSchema`](#fromModuleRuleSchema) to create a usable\n[`Rule`](#Rule). Use \"with\\*\" functions from this module, like\n[`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) or [`withSimpleImportVisitor`](#withSimpleImportVisitor)\nto make it report something.\n\nThe first argument is the rule name. I _highly_ recommend naming it just like the\nmodule name (including all the `.` there may be).\n\nThe second argument is the initial `moduleContext`, i.e. the data that the rule will\naccumulate as the module will be traversed, and allows the rule to know/remember\nwhat happens in other parts of the module. If you don't need a context, I\nrecommend specifying `()`, and using functions from this module with names\nstarting with \"withSimple\".\n\n**NOTE**: Do not store functions, JSON values or regular expressions in your contexts, as they will be\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\n module My.Rule.Name exposing (rule)\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"My.Rule.Name\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\nIf you do need information from other parts of the module, then you should specify\nan initial context, and I recommend using \"with\\*\" functions without \"Simple\" in\ntheir name, like [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\n[`withImportVisitor`](#withImportVisitor) or [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUnusedVariables\" initialContext\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.withImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n type alias Context =\n { declaredVariables : List String\n , usedVariables : List String\n }\n\n initialContext : Context\n initialContext =\n { declaredVariables = [], usedVariables = [] }\n\n","type":"String.String -> moduleContext -> Review.Rule.ModuleRuleSchema { canCollectProjectData : () } moduleContext"},{"name":"newModuleRuleSchemaUsingContextCreator","comment":" Same as [`newModuleRuleSchema`](#newModuleRuleSchema), except that you can request for data to help initialize the context.\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\n module My.Rule.Name exposing (rule)\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"My.Rule.Name\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\nIf you do need information from other parts of the module, then you should specify\nan initial context, and I recommend using \"with\\*\" functions without \"Simple\" in\ntheir name, like [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\n[`withImportVisitor`](#withImportVisitor) or [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchemaUsingContextCreator \"Rule.Name\" contextCreator\n -- visitors\n |> Rule.fromModuleRuleSchema\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { hasTodoBeenImported = False\n , hasToStringBeenImported = False\n , isInSourceDirectories = Rule.isInSourceDirectories metadata\n }\n )\n |> Rule.withMetadata\n\n","type":"String.String -> Review.Rule.ContextCreator () moduleContext -> Review.Rule.ModuleRuleSchema {} moduleContext"},{"name":"newProjectRuleSchema","comment":" Creates a schema for a project rule. Will require adding project visitors and calling\n[`fromProjectRuleSchema`](#fromProjectRuleSchema) to create a usable [`Rule`](#Rule).\n\nThe first argument is the rule name. I _highly_ recommend naming it just like the\nmodule name (including all the `.` there may be).\n\nThe second argument is the initial `projectContext`, i.e. the data that the rule will\naccumulate as the project will be traversed, and allows the rule to know/remember\nwhat happens in other parts of the project.\n\n**NOTE**: Do not store functions, JSON values or regular expressions in your contexts, as they will be\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\nProject rules traverse the project in the following order:\n\n - Read and/or report errors in project files\n - The `elm.json` file, visited by [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)\n - The `README.md` file, visited by [`withReadmeProjectVisitor`](#withReadmeProjectVisitor)\n - The definition for dependencies, visited by [`withDependenciesProjectVisitor`](#withDependenciesProjectVisitor)\n - The Elm modules one by one, visited by [`withModuleVisitor`](#withModuleVisitor),\n following the same traversal order as for module rules but without reading the project files (`elm.json`, ...).\n - A final evaluation when all modules have been visited, using [`withFinalProjectEvaluation`](#withFinalProjectEvaluation)\n\nEvaluating/visiting a node means two things:\n\n - Detecting patterns and reporting errors\n - Collecting data in a \"context\", which will be either a `projectContext` or a `moduleContext` depending on the part of the project being visited, to have more information available in a later\n part of the traversal evaluation.\n\n","type":"String.String -> projectContext -> Review.Rule.ProjectRuleSchema { canAddModuleVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"review","comment":" Review a project and gives back the errors raised by the given rules.\n\nNote that you won't need to use this function when writing a rule. You should\nonly need it if you try to make `elm-review` run in a new environment.\n\n import Review.Project as Project exposing (Project, ProjectModule)\n import Review.Rule as Rule exposing (Rule)\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n\n project : Project\n project =\n Project.new\n |> Project.addModule { path = \"src/A.elm\", source = \"module A exposing (a)\\na = 1\" }\n |> Project.addModule { path = \"src/B.elm\", source = \"module B exposing (b)\\nb = 1\" }\n\n doReview =\n let\n ( errors, rulesWithCachedValues ) =\n Rule.review rules project\n in\n doSomethingWithTheseValues\n\nThe resulting `List Rule` is the same list of rules given as input, but with an\nupdated internal cache to make it faster to re-run the rules on the same project.\nIf you plan on re-reviewing with the same rules and project, for instance to\nreview the project after a file has changed, you may want to store the rules in\nyour `Model`.\n\nThe rules are functions, so doing so will make your model unable to be\nexported/imported with `elm/browser`'s debugger, and may cause a crash if you try\nto compare them or the model that holds them.\n\n","type":"List.List Review.Rule.Rule -> Review.Project.Internal.Project -> ( List.List Review.Rule.ReviewError, List.List Review.Rule.Rule )"},{"name":"reviewWithPrecollectionOfData","comment":" Review a project and gives back the errors raised by the given rules.\n\nNote that you won't need to use this function when writing a rule. You should\nonly need it if you try to make `elm-review` run in a new environment.\n\n import Review.Project as Project exposing (Project, ProjectModule)\n import Review.Rule as Rule exposing (Rule)\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n\n project : Project\n project =\n Project.new\n |> Project.addModule { path = \"src/A.elm\", source = \"module A exposing (a)\\na = 1\" }\n |> Project.addModule { path = \"src/B.elm\", source = \"module B exposing (b)\\nb = 1\" }\n\n doReview =\n let\n ( errors, rulesWithCachedValues ) =\n Rule.reviewWithPrecollectionOfData rules Nothing project\n in\n doSomethingWithTheseValues\n\nThe resulting `List Rule` is the same list of rules given as input, but with an\nupdated internal cache to make it faster to re-run the rules on the same project.\nIf you plan on re-reviewing with the same rules and project, for instance to\nreview the project after a file has changed, you may want to store the rules in\nyour `Model`.\n\nThe rules are functions, so doing so will make your model unable to be\nexported/imported with `elm/browser`'s debugger, and may cause a crash if you try\nto compare them or the model that holds them.\n\n","type":"List.List Review.Rule.Rule -> Maybe.Maybe Review.Rule.ProjectData -> Review.Project.Internal.Project -> { errors : List.List Review.Rule.ReviewError, rules : List.List Review.Rule.Rule, projectData : Maybe.Maybe Review.Rule.ProjectData }"},{"name":"ruleName","comment":" Get the name of a rule.\n\nYou should not have to use this when writing a rule.\n\n","type":"Review.Rule.Rule -> String.String"},{"name":"withCommentsVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's comments, collect data in\nthe `context` and/or report patterns.\n\nThis visitor will give you access to the list of all comments in the module all at once.\n\nTip: If you do not need to collect data in this visitor, you may wish to use the\nsimpler [`withSimpleCommentsVisitor`](#withSimpleCommentsVisitor) function.\n\n","type":"(List.List (Elm.Syntax.Node.Node String.String) -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withContextFromImportedModules","comment":" Allows the rule to have access to the context of the modules imported by the\ncurrently visited module. You can use for instance to know what is exposed in a\ndifferent module.\n\nWhen you finish analyzing a module, the `moduleContext` is turned into a `projectContext`\nthrough [`fromModuleToProject`](#newProjectRuleSchema). Before analyzing a module,\nthe `projectContext`s of its imported modules get folded into a single one\nstarting with the initial context (that may have visited the\n[`elm.json` file](#withElmJsonProjectVisitor) and/or the [project's dependencies](#withDependenciesProjectVisitor))\nusing [`foldProjectContexts`](#newProjectRuleSchema).\n\nIf there is information about another module that you wish to access, you should\ntherefore store it in the `moduleContext`, and have it persist when transitioning\nto a `projectContext` and back to a `moduleContext`.\n\nYou can only access data from imported modules, not from modules that import the\ncurrent module. If you need to do so, I suggest collecting all the information\nyou need, and re-evaluate if from [the final project evaluation function](#withFinalProjectEvaluation).\n\nIf you don't use this function, you will only be able to access the contents of\nthe initial context. The benefit is that when re-analyzing the project, after a\nfix or when a file was changed in watch mode, much less work will need to be done\nand the analysis will be much faster, because we know other files won't influence\nthe results of other modules' analysis.\n\n","type":"Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext"},{"name":"withDeclarationEnterVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nThe following example forbids exposing a function or a value without it having a\ntype annotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Exposing as Exposing\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type ExposedFunctions\n = All\n | OnlySome (List String)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingDocumentationForExposedFunctions\" (OnlySome [])\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withDeclarationEnterVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n moduleDefinitionVisitor node context =\n case Node.value node |> Module.exposingList of\n Exposing.All _ ->\n ( [], All )\n\n Exposing.Explicit exposedValues ->\n ( [], OnlySome (List.filterMap exposedFunctionName exposedValues) )\n\n exposedFunctionName : Node Exposing.TopLevelExpose -> Maybe String\n exposedFunctionName value =\n case Node.value value of\n Exposing.FunctionExpose functionName ->\n Just functionName\n\n _ ->\n Nothing\n\n declarationVisitor : Node Declaration -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n declarationVisitor node direction context =\n case Node.value node of\n Declaration.FunctionDeclaration { documentation, declaration } ->\n let\n functionName : String\n functionName =\n Node.value declaration |> .name |> Node.value\n in\n if documentation == Nothing && isExposed context functionName then\n ( [ Rule.error\n { message = \"Exposed function \" ++ functionName ++ \" is missing a type annotation\"\n , details =\n [ \"Type annotations are very helpful for people who use the module. It can give a lot of information without having to read the contents of the function.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n isExposed : ExposedFunctions -> String -> Bool\n isExposed exposedFunctions name =\n case exposedFunctions of\n All ->\n True\n\n OnlySome exposedList ->\n List.member name exposedList\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationExitVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nThe following example reports unused parameters from top-level declarations.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withDeclarationEnterVisitor declarationEnterVisitor\n |> Rule.withDeclarationExitVisitor declarationExitVisitor\n -- Omitted, but this marks parameters as used\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n declarationEnterVisitor : Node Declaration -> fContext -> ( List (Error {}), Context )\n declarationEnterVisitor node context =\n case Node.value node of\n Declaration.FunctionDeclaration function ->\n ( [], registerArguments context function )\n\n _ ->\n ( [], context )\n\n declarationExitVisitor : Node Declaration -> Context -> ( List (Error {}), Context )\n declarationExitVisitor node context =\n case Node.value node of\n -- When exiting the function expression, report the parameters that were not used.\n Declaration.FunctionDeclaration function ->\n ( unusedParameters context |> List.map createError, removeArguments context )\n\n _ ->\n ( [], context )\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationListVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns.\n\nIt is similar to [withDeclarationVisitor](#withDeclarationVisitor), but the\nvisitor used with this function is called before the visitor added with\n[withDeclarationVisitor](#withDeclarationVisitor). You can use this visitor in\norder to look ahead and add the module's types and variables into your context,\nbefore visiting the contents of the module using [withDeclarationVisitor](#withDeclarationVisitor)\nand [withExpressionEnterVisitor](#withExpressionEnterVisitor). Otherwise, using\n[withDeclarationVisitor](#withDeclarationVisitor) is probably a simpler choice.\n\n","type":"(List.List (Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration) -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationVisitor","comment":" **DEPRECATED**\n\nUse [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor) instead.\nIn the next major version, this function will be removed and [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) will be renamed to `withDeclarationVisitor`.\n\n**/DEPRECATED**\n\nAdd a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nContrary to [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor), the\nvisitor function will be called twice with different [`Direction`](#Direction)\nvalues. It will be visited with `OnEnter`, then the children will be visited,\nand then it will be visited again with `OnExit`. If you do not check the value of\nthe `Direction` parameter, you might end up with duplicate errors and/or an\nunexpected `moduleContext`. Read more about [`Direction` here](#Direction).\n\nThe following example forbids exposing a function or a value without it having a\ntype annotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Exposing as Exposing\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type ExposedFunctions\n = All\n | OnlySome (List String)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingDocumentationForExposedFunctions\" (OnlySome [])\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withDeclarationVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n moduleDefinitionVisitor node context =\n case Node.value node |> Module.exposingList of\n Exposing.All _ ->\n ( [], All )\n\n Exposing.Explicit exposedValues ->\n ( [], OnlySome (List.filterMap exposedFunctionName exposedValues) )\n\n exposedFunctionName : Node Exposing.TopLevelExpose -> Maybe String\n exposedFunctionName value =\n case Node.value value of\n Exposing.FunctionExpose functionName ->\n Just functionName\n\n _ ->\n Nothing\n\n declarationVisitor : Node Declaration -> Direction -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n declarationVisitor node direction context =\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Declaration.FunctionDeclaration { documentation, declaration } ) ->\n let\n functionName : String\n functionName =\n Node.value declaration |> .name |> Node.value\n in\n if documentation == Nothing && isExposed context functionName then\n ( [ Rule.error\n { message = \"Exposed function \" ++ functionName ++ \" is missing a type annotation\"\n , details =\n [ \"Type annotations are very helpful for people who use the module. It can give a lot of information without having to read the contents of the function.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n isExposed : ExposedFunctions -> String -> Bool\n isExposed exposedFunctions name =\n case exposedFunctions of\n All ->\n True\n\n OnlySome exposedList ->\n List.member name exposedList\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> Review.Rule.Direction -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDependenciesModuleVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[dependencies](./Review-Project-Dependency).\n\nYou can use this look at the modules contained in dependencies, which can make the rule very precise when it targets\nspecific functions.\n\n","type":"(Dict.Dict String.String Review.Project.Dependency.Dependency -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withDependenciesProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[dependencies](./Review-Project-Dependency).\n\nIt works exactly like [`withDependenciesModuleVisitor`](#withDependenciesModuleVisitor). The visitor will be called before any\nmodule is evaluated.\n\n","type":"(Dict.Dict String.String Review.Project.Dependency.Dependency -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withElmJsonModuleVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the project's\n[`elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project) file.\n\nThe following example forbids exposing a module in an \"Internal\" directory in your `elm.json` file.\n\n import Elm.Module\n import Elm.Project\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n Maybe Elm.Project.Project\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"DoNoExposeInternalModules\" Nothing\n |> Rule.withElmJsonModuleVisitor elmJsonVisitor\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.fromModuleRuleSchema\n\n elmJsonVisitor : Maybe Elm.Project.Project -> Context -> Context\n elmJsonVisitor elmJson context =\n elmJson\n\n moduleDefinitionVisitor : Node Module -> Context -> ( List (Error {}), Context )\n moduleDefinitionVisitor node context =\n let\n moduleName : List String\n moduleName =\n Node.value node |> Module.moduleName\n in\n if List.member \"Internal\" moduleName then\n case context of\n Just (Elm.Project.Package { exposed }) ->\n let\n exposedModules : List String\n exposedModules =\n case exposed of\n Elm.Project.ExposedList names ->\n names\n |> List.map Elm.Module.toString\n\n Elm.Project.ExposedDict fakeDict ->\n fakeDict\n |> List.concatMap Tuple.second\n |> List.map Elm.Module.toString\n in\n if List.member (String.join \".\" moduleName) exposedModules then\n ( [ Rule.error \"Do not expose modules in `Internal` as part of the public API\" (Node.range node) ], context )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n else\n ( [], context )\n\n","type":"(Maybe.Maybe Elm.Project.Project -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withElmJsonProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[`elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project) file.\n\nIt works exactly like [`withElmJsonModuleVisitor`](#withElmJsonModuleVisitor).\nThe visitor will be called before any module is evaluated.\n\n","type":"(Maybe.Maybe { elmJsonKey : Review.Rule.ElmJsonKey, project : Elm.Project.Project } -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withExpressionEnterVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withExpressionVisitor`](#withExpressionVisitor), the\nvisitor function will be called only once, when the expression is \"entered\",\nmeaning before its children are visited.\n\nThe following example forbids the use of `Debug.log` even when it is imported like\n`import Debug exposing (log)`.\n\n import Elm.Syntax.Exposing as Exposing exposing (TopLevelExpose)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = DebugLogWasNotImported\n | DebugLogWasImported\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withImportVisitor importVisitor\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case ( Node.value node |> .moduleName |> Node.value, (Node.value node).exposingList |> Maybe.map Node.value ) of\n ( [ \"Debug\" ], Just (Exposing.All _) ) ->\n ( [], DebugLogWasImported )\n\n ( [ \"Debug\" ], Just (Exposing.Explicit exposedFunctions) ) ->\n let\n isLogFunction : Node Exposing.TopLevelExpose -> Bool\n isLogFunction exposeNode =\n case Node.value exposeNode of\n Exposing.FunctionExpose \"log\" ->\n True\n\n _ ->\n False\n in\n if List.any isLogFunction exposedFunctions then\n ( [], DebugLogWasImported )\n\n else\n ( [], DebugLogWasNotImported )\n\n _ ->\n ( [], DebugLogWasNotImported )\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case context of\n DebugLogWasNotImported ->\n ( [], context )\n\n DebugLogWasImported ->\n case Node.value node of\n Expression.FunctionOrValue [] \"log\" ->\n ( [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withExpressionExitVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withExpressionEnterVisitor`](#withExpressionEnterVisitor), the\nvisitor function will be called when the expression is \"exited\",\nmeaning after its children are visited.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withExpressionEnterVisitor expressionEnterVisitor\n |> Rule.withExpressionExitVisitor expressionExitVisitor\n |> Rule.fromModuleRuleSchema\n\n expressionEnterVisitor : Node Expression -> fContext -> ( List (Error {}), Context )\n expressionEnterVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue moduleName name ->\n ( [], markVariableAsUsed context name )\n\n -- Find variables declared in let expression\n Expression.LetExpression letBlock ->\n ( [], registerVariables context letBlock )\n\n _ ->\n ( [], context )\n\n expressionExitVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionExitVisitor node context =\n case Node.value node of\n -- When exiting the let expression, report the variables that were not used.\n Expression.LetExpression _ ->\n ( unusedVariables context |> List.map createError, removeVariables context )\n\n _ ->\n ( [], context )\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withExpressionVisitor","comment":" **DEPRECATED**\n\nUse [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor) instead.\nIn the next major version, this function will be removed and [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) will be renamed to `withExpressionVisitor`.\n\n**/DEPRECATED**\n\nAdd a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor), the\nvisitor function will be called twice with different [`Direction`](#Direction)\nvalues. It will be visited with `OnEnter`, then the children will be visited,\nand then it will be visited again with `OnExit`. If you do not check the value of\nthe `Direction` parameter, you might end up with duplicate errors and/or an\nunexpected `moduleContext`. Read more about [`Direction` here](#Direction).\n\nThe following example forbids the use of `Debug.log` even when it is imported like\n`import Debug exposing (log)`.\n\n import Elm.Syntax.Exposing as Exposing exposing (TopLevelExpose)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = DebugLogWasNotImported\n | DebugLogWasImported\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withImportVisitor importVisitor\n |> Rule.withExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case ( Node.value node |> .moduleName |> Node.value, (Node.value node).exposingList |> Maybe.map Node.value ) of\n ( [ \"Debug\" ], Just (Exposing.All _) ) ->\n ( [], DebugLogWasImported )\n\n ( [ \"Debug\" ], Just (Exposing.Explicit exposedFunctions) ) ->\n let\n isLogFunction : Node Exposing.TopLevelExpose -> Bool\n isLogFunction exposeNode =\n case Node.value exposeNode of\n Exposing.FunctionExpose \"log\" ->\n True\n\n _ ->\n False\n in\n if List.any isLogFunction exposedFunctions then\n ( [], DebugLogWasImported )\n\n else\n ( [], DebugLogWasNotImported )\n\n _ ->\n ( [], DebugLogWasNotImported )\n\n expressionVisitor : Node Expression -> Direction -> Context -> ( List (Error {}), Context )\n expressionVisitor node direction context =\n case context of\n DebugLogWasNotImported ->\n ( [], context )\n\n DebugLogWasImported ->\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Expression.FunctionOrValue [] \"log\" ) ->\n ( [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> Review.Rule.Direction -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withFinalModuleEvaluation","comment":" Add a function that makes a final evaluation of the module based only on the\ndata that was collected in the `moduleContext`. This can be useful if you can't or if\nit is hard to determine something as you traverse the module.\n\nThe following example forbids importing both `Element` (`elm-ui`) and\n`Html.Styled` (`elm-css`). Note that this is the same one written in the example\nfor [`withImportVisitor`](#withImportVisitor), but using [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Dict as Dict exposing (Dict)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Elm.Syntax.Range exposing (Range)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n Dict (List String) Range\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUsingBothHtmlAndHtmlStyled\" Dict.empty\n |> Rule.withImportVisitor importVisitor\n |> Rule.withFinalModuleEvaluation finalEvaluation\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n ( [], Dict.insert (Node.value node |> .moduleName |> Node.value) (Node.range node) context )\n\n finalEvaluation : Context -> List (Error {})\n finalEvaluation context =\n case ( Dict.get [ \"Element\" ] context, Dict.get [ \"Html\", \"Styled\" ] context ) of\n ( Just elmUiRange, Just _ ) ->\n [ Rule.error\n { message = \"Do not use both `elm-ui` and `elm-css`\"\n , details = [ \"At fruits.com, we use `elm-ui` in the dashboard application, and `elm-css` in the rest of the code. We want to use `elm-ui` in our new projects, but in projects using `elm-css`, we don't want to use both libraries to keep things simple.\" ]\n }\n elmUiRange\n ]\n\n _ ->\n []\n\n","type":"(moduleContext -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withFinalProjectEvaluation","comment":" Add a function that makes a final evaluation of the project based only on the\ndata that was collected in the `projectContext`. This can be useful if you can't report something until you have visited\nall the modules in the project.\n\nIt works similarly [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n**NOTE**: Do not create errors using the [`error`](#error) function using `withFinalProjectEvaluation`, but using [`errorForModule`](#errorForModule)\ninstead. When the project is evaluated in this function, you are not in the \"context\" of an Elm module (the idiomatic \"context\", not `projectContext` or `moduleContext`).\nThat means that if you call [`error`](#error), we won't know which module to associate the error to.\n\n","type":"(projectContext -> List.List (Review.Rule.Error { useErrorForModule : () })) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext"},{"name":"withImportVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[import statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Import)\n(`import Html as H exposing (div)`) in order of their definition, collect data\nin the `context` and/or report patterns.\n\nThe following example forbids importing both `Element` (`elm-ui`) and\n`Html.Styled` (`elm-css`).\n\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n { elmUiWasImported : Bool\n , elmCssWasImported : Bool\n }\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUsingBothHtmlAndHtmlStyled\" initialContext\n |> Rule.withImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n initialContext : Context\n initialContext =\n { elmUiWasImported = False\n , elmCssWasImported = False\n }\n\n error : Node Import -> Error {}\n error node =\n Rule.error\n { message = \"Do not use both `elm-ui` and `elm-css`\"\n , details = [ \"At fruits.com, we use `elm-ui` in the dashboard application, and `elm-css` in the rest of the code. We want to use `elm-ui` in our new projects, but in projects using `elm-css`, we don't want to use both libraries to keep things simple.\" ]\n }\n (Node.range node)\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case Node.value node |> .moduleName |> Node.value of\n [ \"Element\" ] ->\n if context.elmCssWasImported then\n ( [ error node ]\n , { context | elmUiWasImported = True }\n )\n\n else\n ( [ error node ]\n , { context | elmUiWasImported = True }\n )\n\n [ \"Html\", \"Styled\" ] ->\n if context.elmUiWasImported then\n ( [ error node ]\n , { context | elmCssWasImported = True }\n )\n\n else\n ( [ error node ]\n , { context | elmCssWasImported = True }\n )\n\n _ ->\n ( [], context )\n\nThis example was written in a different way in the example for [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleImportVisitor`](#withSimpleImportVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Import.Import -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withMetadata","comment":" Request metadata about the module.\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { moduleName = Rule.moduleNameFromMetadata metadata\n , moduleNameNode = Rule.moduleNameNodeFromMetadata metadata\n , isInSourceDirectories = Rule.isInSourceDirectories metadata\n\n -- ...other fields\n }\n )\n |> Rule.withMetadata\n\n","type":"Review.Rule.ContextCreator Review.Rule.Metadata (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleContext","comment":" Specify, if the project rule has a [module visitor](#withModuleVisitor), how to:\n\n - convert a project context to a module context, through [`fromProjectToModule`]\n - convert a module context to a project context, through [`fromModuleToProject`]\n - fold (merge) project contexts, through [`foldProjectContexts`]\n\n**NOTE**: I suggest reading the section about [`foldProjectContexts`] carefully,\nas it is one whose implementation you will need to do carefully.\n\nIn project rules, we separate the context related to the analysis of the project\nas a whole and the context related to the analysis of a single module into a\n`projectContext` and a `moduleContext` respectively. We do this because in most\nproject rules you won't need all the data from the `projectContext` to analyze a\nmodule, and some data from the module context will not make sense inside the\nproject context.\n\nWhen visiting modules, `elm-review` follows a kind of map-reduce architecture.\nThe idea is the following: it starts with an initial `projectContext` and collects data\nfrom project-related files into it. Then, it visits every module with an initial\n`moduleContext` derived from a `projectContext`. At the end of a module's visit,\nthe final `moduleContext` will be transformed (\"map\") to a `projectContext`.\nAll or some of the `projectContext`s will then be folded into a single one,\nbefore being used in the [final project evaluation] or to compute another module's\ninitial `moduleContext`.\n\nThis will help make the result of the review as consistent as possible, by\nhaving the results be independent of the order the modules are visited. This also\ngives internal guarantees as to what needs to be re-computed when re-analyzing\nthe project, which leads to huge performance boosts in watch mode or after fixes\nhave been applied.\n\nThe following sections will explain each function, and will be summarized by an\nexample.\n\n\n### `fromProjectToModule`\n\nThe initial `moduleContext` of the module visitor is computed using `fromProjectToModule`\nfrom a `projectContext`. By default, this `projectContext` will be the result of\nvisiting the project-related files (`elm.json`, `README.md`, ...).\nIf [`withContextFromImportedModules`] was used, then the value will be this last\n`projectContext`, folded with each imported module's resulting `projectContext`,\nusing [`foldProjectContexts`].\n\nThe [`ModuleKey`] will allow you to report errors for this specific module\nusing [`errorForModule`](#errorForModule) from the [final project evaluation] or\nwhile visiting another module. If you plan to do that, you should store this in\nthe `moduleContext`. You can also get it from [`fromModuleToProject`], so choose\nwhat's most convenient.\n\nThe [`Node`] containing the module name is passed for convenience, so you don't\nhave to visit the module definition just to get the module name. Just like what\nit is in [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-ModuleName),\nthe value will be `[ \"My\", \"Module\" ]` if the module name is `My.Module`.\n\n\n### `fromModuleToProject`\n\nWhen a module has finished being analyzed, the final `moduleContext` will be\nconverted into a `projectContext`, so that it can later be folded with the other\nproject contexts using `foldProjectContexts`. The resulting `projectContext`\nwill be fed into the [final project evaluation] and potentially into\n[`fromProjectToModule`] for modules that import the current one.\n\nSimilarly to `fromProjectToModule`, the [`Node`] containing the module name and\nthe [`ModuleKey`] are passed for convenience, so you don't have to store them in\nthe `moduleContext` only to store them in the `projectContext`.\n\n\n### `foldProjectContexts`\n\nThis function folds two `projectContext` into one. This function requires a few\ntraits to always be true.\n\n - `projectContext`s should be \"merged\" together, not \"subtracted\". If for instance\n you want to detect the unused exports of a module, do not remove a declared\n export when you have found it used. Instead, store and accumulate the declared\n and used functions (both probably as `Set`s or `Dict`s), and in the final evaluation,\n filter out the declared functions if they are in the set of used functions.\n - The order of folding should not matter: `foldProjectContexts b (foldProjectContexts a initial)`\n should equal `foldProjectContexts a (foldProjectContexts b initial)`.\n [`List.concat`](https://package.elm-lang.org/packages/elm/core/latest/List#concat).\n - Folding an element twice into another should give the same result as folding\n it once. In other words, `foldProjectContexts a (foldProjectContexts a initial)`\n should equal `foldProjectContexts a initial`. You will likely need to use functions\n like [`Set.union`](https://package.elm-lang.org/packages/elm/core/latest/Set#union)\n and [`Dict.union`](https://package.elm-lang.org/packages/elm/core/latest/Dict#union)\n over addition and functions like\n [`List.concat`](https://package.elm-lang.org/packages/elm/core/latest/List#concat).\n\nIt is not necessary for the function to be commutative (i.e. that\n`foldProjectContexts a b` equals `foldProjectContexts b a`). It is fine to take\nthe value from the \"initial\" `projectContext` and ignore the other one, especially\nfor data computed in the project-related visitors (for which you will probably\ndefine a dummy value in the `fromModuleToProject` function). If it helps, imagine\nthat the second argument is the initial `projectContext`, or that it is an accumulator\njust like in `List.foldl`.\n\n\n### Summary example - Reporting unused exported functions\n\nAs an example, we will write a rule that reports functions that get exported\nbut are unused in the rest of the project.\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newProjectRuleSchema \"NoUnusedExportedFunctions\" initialProjectContext\n -- Omitted, but this will collect the list of exposed modules for packages.\n -- We don't want to report functions that are exposed\n |> Rule.withElmJsonProjectVisitor elmJsonVisitor\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContext\n { fromProjectToModule = fromProjectToModule\n , fromModuleToProject = fromModuleToProject\n , foldProjectContexts = foldProjectContexts\n }\n |> Rule.withFinalProjectEvaluation finalEvaluationForProject\n |> Rule.fromProjectRuleSchema\n\n moduleVisitor :\n Rule.ModuleRuleSchema {} ModuleContext\n -> Rule.ModuleRuleSchema { hasAtLeastOneVisitor : () } ModuleContext\n moduleVisitor schema =\n schema\n -- Omitted, but this will collect the exposed functions\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n -- Omitted, but this will collect uses of exported functions\n |> Rule.withExpressionEnterVisitor expressionVisitor\n\n type alias ProjectContext =\n { -- Modules exposed by the package, that we should not report\n exposedModules : Set ModuleName\n , exposedFunctions :\n -- An entry for each module\n Dict ModuleName\n { -- To report errors in this module\n moduleKey : Rule.ModuleKey\n\n -- An entry for each function with its location\n , exposed : Dict String Range\n }\n , used : Set ( ModuleName, String )\n }\n\n type alias ModuleContext =\n { isExposed : Bool\n , exposed : Dict String Range\n , used : Set ( ModuleName, String )\n }\n\n initialProjectContext : ProjectContext\n initialProjectContext =\n { exposedModules = Set.empty\n , modules = Dict.empty\n , used = Set.empty\n }\n\n fromProjectToModule : Rule.ModuleKey -> Node ModuleName -> ProjectContext -> ModuleContext\n fromProjectToModule moduleKey moduleName projectContext =\n { isExposed = Set.member (Node.value moduleName) projectContext.exposedModules\n , exposed = Dict.empty\n , used = Set.empty\n }\n\n fromModuleToProject : Rule.ModuleKey -> Node ModuleName -> ModuleContext -> ProjectContext\n fromModuleToProject moduleKey moduleName moduleContext =\n { -- We don't care about this value, we'll take\n -- the one from the initial context when folding\n exposedModules = Set.empty\n , exposedFunctions =\n if moduleContext.isExposed then\n -- If the module is exposed, don't collect the exported functions\n Dict.empty\n\n else\n -- Create a dictionary with all the exposed functions, associated to\n -- the module that was just visited\n Dict.singleton\n (Node.value moduleName)\n { moduleKey = moduleKey\n , exposed = moduleContext.exposed\n }\n , used = moduleContext.used\n }\n\n foldProjectContexts : ProjectContext -> ProjectContext -> ProjectContext\n foldProjectContexts newContext previousContext =\n { -- Always take the one from the \"initial\" context,\n -- which is always the second argument\n exposedModules = previousContext.exposedModules\n\n -- Collect the exposed functions from the new context and the previous one.\n -- We could use `Dict.merge`, but in this case, that doesn't change anything\n , exposedFunctions = Dict.union previousContext.modules newContext.modules\n\n -- Collect the used functions from the new context and the previous one\n , used = Set.union newContext.used previousContext.used\n }\n\n finalEvaluationForProject : ProjectContext -> List (Error { useErrorForModule : () })\n finalEvaluationForProject projectContext =\n -- Implementation of `unusedFunctions` omitted, but it returns the list\n -- of unused functions, along with the associated module key and range\n unusedFunctions projectContext\n |> List.map\n (\\{ moduleKey, functionName, range } ->\n Rule.errorForModule moduleKey\n { message = \"Function `\" ++ functionName ++ \"` is never used\"\n , details = [ \"\" ]\n }\n range\n )\n\n[`ModuleKey`]: #ModuleKey\n[`Node`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node\n[`fromProjectToModule`]: #-fromprojecttomodule-\n[`fromModuleToProject`]: #-frommoduletoproject-\n[`foldProjectContexts`]: #-foldprojectcontexts-\n[final project evaluation]: #withFinalProjectEvaluation\n[`withContextFromImportedModules`]: #withContextFromImportedModules\n\n","type":"{ fromProjectToModule : Review.Rule.ModuleKey -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName -> projectContext -> moduleContext, fromModuleToProject : Review.Rule.ModuleKey -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName -> moduleContext -> projectContext, foldProjectContexts : projectContext -> projectContext -> projectContext } -> Review.Rule.ProjectRuleSchema { schemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"withModuleContextUsingContextCreator","comment":" Use a [`ContextCreator`](#ContextCreator) to initialize your `moduleContext` and `projectContext`. This will allow\nyou to request more information\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newProjectRuleSchema \"NoMissingSubscriptionsCall\" initialProjectContext\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContextUsingContextCreator\n { fromProjectToModule = Rule.initContextCreator fromProjectToModule\n , fromModuleToProject =\n Rule.initContextCreator fromModuleToProject\n |> Rule.withModuleKey\n |> Rule.withMetadata\n , foldProjectContexts = foldProjectContexts\n }\n |> Rule.fromProjectRuleSchema\n\n fromProjectToModule : ProjectContext -> ModuleContext\n fromProjectToModule projectContext =\n { -- something\n }\n\n fromModuleToProject : Rule.ModuleKey -> Metadata -> ModuleContext -> ProjectContext\n fromModuleToProject moduleKey Metadata moduleContext =\n { moduleKeys = Dict.singleton (Rule.moduleNameFromMetadata metadata) moduleKey\n }\n\n","type":"{ fromProjectToModule : Review.Rule.ContextCreator projectContext moduleContext, fromModuleToProject : Review.Rule.ContextCreator moduleContext projectContext, foldProjectContexts : projectContext -> projectContext -> projectContext } -> Review.Rule.ProjectRuleSchema { schemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"withModuleDefinitionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[module definition](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Module) (`module SomeModuleName exposing (a, b)`), collect data in the `context` and/or report patterns.\n\nThe following example forbids the use of `Html.button` except in the \"Button\" module.\nThe example is simplified to only forbid the use of the `Html.button` expression.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = HtmlButtonIsAllowed\n | HtmlButtonIsForbidden\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoHtmlButton\" HtmlButtonIsForbidden\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> Context -> ( List (Error {}), Context )\n moduleDefinitionVisitor node context =\n if (Node.value node |> Module.moduleName) == [ \"Button\" ] then\n ( [], HtmlButtonIsAllowed )\n\n else\n ( [], HtmlButtonIsForbidden )\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case context of\n HtmlButtonIsAllowed ->\n ( [], context )\n\n HtmlButtonIsForbidden ->\n case Node.value node of\n Expression.FunctionOrValue [ \"Html\" ] \"button\" ->\n ( [ Rule.error\n { message = \"Do not use `Html.button` directly\"\n , details = [ \"At fruits.com, we've built a nice `Button` module that suits our needs better. Using this module instead of `Html.button` ensures we have a consistent button experience across the website.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect data in this visitor, you may wish to use the\nsimpler [`withSimpleModuleDefinitionVisitor`](#withSimpleModuleDefinitionVisitor) function.\n\nTip: The rule above is very brittle. What if `button` was imported using `import Html exposing (button)` or `import Html exposing (..)`, or if `Html` was aliased (`import Html as H`)? Then the rule above would\nnot catch and report the use `Html.button`. To handle this, check out [`withModuleNameLookupTable`](#withModuleNameLookupTable).\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Module.Module -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withModuleKey","comment":" Request the [module key](ModuleKey) for this module.\n\n rule =\n Rule.newProjectRuleSchema \"NoMissingSubscriptionsCall\" initialProjectContext\n |> Scope.addProjectVisitors\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContextUsingContextCreator\n { fromProjectToModule = Rule.initContextCreator fromProjectToModule\n , fromModuleToProject = Rule.initContextCreator fromModuleToProject |> Rule.withModuleKey\n , foldProjectContexts = foldProjectContexts\n }\n\nTODO Make this unavailable to module rules\n\n","type":"Review.Rule.ContextCreator Review.Rule.ModuleKey (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleNameLookupTable","comment":" Requests the module name lookup table for the types and functions inside a module.\n\nWhen encountering a `Expression.FunctionOrValue ModuleName String`, the module name available represents the module name\nthat is in the source code. But that module name can be an alias to a different import, or it can be empty, meaning that\nit refers to a local value or one that has been imported explicitly or implicitly. Resolving which module name the type\nor function can be a bit tricky sometimes, and I recommend against doing it yourself.\n\n`elm-review` computes this for you already. Store this value inside your module context, then use\n[`ModuleNameLookupTable.moduleNameFor`](./Review-ModuleNameLookupTable#moduleNameFor) or\n[`ModuleNameLookupTable.moduleNameAt`](./Review-ModuleNameLookupTable#moduleNameAt) to get the name of the module the\ntype or value comes from.\n\n import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)\n\n type alias Context =\n { lookupTable : ModuleNameLookupTable }\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchemaUsingContextCreator \"NoHtmlButton\" contextCreator\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForFiles [ \"src/Colors.elm\" ]\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\lookupTable () -> { lookupTable = lookupTable })\n |> Rule.withModuleNameLookupTable\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue _ \"color\" ->\n if ModuleNameLookupTable.moduleNameFor context.lookupTable node == Just [ \"Css\" ] then\n ( [ Rule.error\n { message = \"Do not use `Css.color` directly, use the Colors module instead\"\n , details = [ \"We made a module which contains all the available colors of our design system. Use the functions in there instead.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\nNote: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you might want to use this instead.\n\n","type":"Review.Rule.ContextCreator Review.ModuleNameLookupTable.ModuleNameLookupTable (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will\nvisit the project's Elm modules.\n\nA module visitor behaves like a module rule, except that it won't visit the\nproject files, as those have already been seen by other visitors for project rules (such\nas [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)).\n\n`withModuleVisitor` takes a function that takes an already initialized module\nrule schema and adds visitors to it, using the same functions as for building a\n[`ModuleRuleSchema`](#ModuleRuleSchema).\n\nWhen you use `withModuleVisitor`, you will be required to use [`withModuleContext`](#withModuleContext),\nin order to specify how to create a `moduleContext` from a `projectContext` and vice-versa.\n\n","type":"(Review.Rule.ModuleRuleSchema {} moduleContext -> Review.Rule.ModuleRuleSchema { moduleSchemaState | hasAtLeastOneVisitor : () } moduleContext) -> Review.Rule.ProjectRuleSchema { projectSchemaState | canAddModuleVisitor : () } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { projectSchemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext"},{"name":"withReadmeModuleVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit\nthe project's `README.md` file.\n","type":"(Maybe.Maybe String.String -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withReadmeProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit\nthe project's `README.md` file.\n\nIt works exactly like [`withReadmeModuleVisitor`](#withReadmeModuleVisitor).\nThe visitor will be called before any module is evaluated.\n\n","type":"(Maybe.Maybe { readmeKey : Review.Rule.ReadmeKey, content : String.String } -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withSimpleCommentsVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's comments.\n\nThis visitor will give you access to the list of all comments in the module all at once.\n\nThe following example forbids words like \"TODO\" appearing in a comment.\n\n import Elm.Syntax.Node as Node exposing (Node)\n import Elm.Syntax.Range exposing (Range)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoTodoComment\" ()\n |> Rule.withSimpleCommentsVisitor commentsVisitor\n |> Rule.fromModuleRuleSchema\n\n commentsVisitor : List (Node String) -> List (Error {})\n commentsVisitor comments =\n comments\n |> List.concatMap\n (\\commentNode ->\n String.indexes \"TODO\" (Node.value commentNode)\n |> List.map (errorAtPosition (Node.range commentNode))\n )\n\n errorAtPosition : Range -> Int -> Error {}\n errorAtPosition range index =\n Rule.error\n { message = \"TODO needs to be handled\"\n , details = [ \"At fruits.com, we prefer not to have lingering TODO comments. Either fix the TODO now or create an issue for it.\" ]\n }\n -- Here you would ideally only target the TODO keyword\n -- or the rest of the line it appears on,\n -- so you would change `range` using `index`.\n range\n\nNote: `withSimpleCommentsVisitor` is a simplified version of [`withCommentsVisitor`](#withCommentsVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withCommentsVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(List.List (Elm.Syntax.Node.Node String.String) -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleDeclarationVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`)\nand report patterns. The declarations will be visited in the order of their definition.\n\nThe following example forbids declaring a function or a value without a type\nannotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingTypeAnnotation\" ()\n |> Rule.withSimpleDeclarationVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n declarationVisitor : Node Declaration -> List (Error {})\n declarationVisitor node =\n case Node.value node of\n Declaration.FunctionDeclaration { signature, declaration } ->\n case signature of\n Just _ ->\n []\n\n Nothing ->\n let\n functionName : String\n functionName =\n declaration |> Node.value |> .name |> Node.value\n in\n [ Rule.error\n { message = \"Missing type annotation for `\" ++ functionName ++ \"`\"\n , details =\n [ \"Type annotations are very helpful for people who read your code. It can give a lot of information without having to read the contents of the function. When encountering problems, the compiler will also give much more precise and helpful information to help you solve the problem.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\nNote: `withSimpleDeclarationVisitor` is a simplified version of [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor),\nwhich isn't passed a `context` and doesn't return one either. You can use `withSimpleDeclarationVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleExpressionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`). The expressions are visited in pre-order\ndepth-first search, meaning that an expression will be visited, then its first\nchild, the first child's children (and so on), then the second child (and so on).\n\nThe following example forbids using the Debug module.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebug\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node =\n case Node.value node of\n Expression.FunctionOrValue moduleName fnName ->\n if List.member \"Debug\" moduleName then\n [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n\n else\n []\n\n _ ->\n []\n\nNote: `withSimpleExpressionVisitor` is a simplified version of [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\nwhich isn't passed a `context` and doesn't return one either. You can use `withSimpleExpressionVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleImportVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's [import statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Import) (`import Html as H exposing (div)`) in order of their definition and report patterns.\n\nThe following example forbids using the core Html package and suggests using\n`elm-css` instead.\n\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoCoreHtml\" ()\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> List (Error {})\n importVisitor node =\n let\n moduleName : List String\n moduleName =\n node\n |> Node.value\n |> .moduleName\n |> Node.value\n in\n case moduleName of\n [ \"Html\" ] ->\n [ Rule.error\n { message = \"Use `elm-css` instead of the core HTML package.\"\n , details =\n [ \"At fruits.com, we chose to use the `elm-css` package (https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/Css) to build our HTML and CSS rather than the core Html package. To keep things simple, we think it is best to not mix these different libraries.\"\n , \"The API is very similar, but instead of using the `Html` module, use the `Html.Styled`. CSS is then defined using the Html.Styled.Attributes.css function (https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/Html-Styled-Attributes#css).\"\n ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\nNote: `withSimpleImportVisitor` is a simplified version of [`withImportVisitor`](#withImportVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withSimpleImportVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Import.Import -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleModuleDefinitionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's [module definition](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Module) (`module SomeModuleName exposing (a, b)`) and report patterns.\n\nThe following example forbids having `_` in any part of a module name.\n\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUnderscoreInModuleName\" ()\n |> Rule.withSimpleModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> List (Error {})\n moduleDefinitionVisitor node =\n if List.any (String.contains \"\") (Node.value node |> Module.moduleName) then\n [ Rule.error\n { message = \"Do not use `_` in a module name\"\n , details = [ \"By convention, Elm modules names use Pascal case (like `MyModuleName`). Please rename your module using this format.\" ]\n }\n (Node.range node)\n ]\n\n else\n []\n\nNote: `withSimpleModuleDefinitionVisitor` is a simplified version of [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withSimpleModuleDefinitionVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Module.Module -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"}],"binops":[]},{"name":"Review.Test","comment":" Module that helps you test your rules, using [`elm-test`](https://package.elm-lang.org/packages/elm-explorations/test/latest/).\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should not report anything when \" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = foo n\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n , test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\" \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\n\n# Strategies for effective testing\n\n\n## Use Test-Driven Development\n\nWriting a rule is a process that works really well with the Test-Driven\nDevelopment process loop, which is:\n\n - Before writing any code, write a failing test.\n - Run the test and make sure that it is failing, otherwise you can't be\n sure that the test is well-written.\n - Write the simplest (almost stupid) code to make the test pass\n - Run the tests again and make sure that the test is passing, and that you\n didn't break any previous tests\n - Optionally, refactor your code but be sure not to change the behavior of the\n implementation. You should not add support for new patterns, as you will\n want to write tests for those first.\n\nThen repeat for every pattern yoy wish to handle.\n\n\n## Have a good title\n\nA good test title explains\n\n - what is tested - Probably the rule, but making it explicit\n in a [`describe`](https://package.elm-lang.org/packages/elm-explorations/test/latest/Test#describe)\n might improve your test report. Or maybe you are testing a sub-part of the rule,\n and you can name it explictly.\n - what should happen: (not) reporting an error, fix by , ...\n - when: what is the situation that this test sets up?\n\nIdeally, by only reading through the test titles, someone else should be able to\nrewrite the rule you are testing.\n\n\n## What should you test?\n\nYou should test the scenarios where you expect the rule to report something. At\nthe same time, you should also test when it shouldn't. I encourage writing tests\nto make sure that things that are similar to what you want to report are not\nreported.\n\nFor instance, if you wish to report uses of variables named `foo`, write a test\nthat ensures that the use of variables named differently does not get reported.\n\nTests are pretty cheap, and in the case of rules, it is probably better to have\ntoo many tests rather than too few, since the behavior of a rule rarely changes\ndrastically.\n\n\n# Design goals\n\nIf you are interested, you can read\n[the design goals](https://github.com/jfmengels/elm-review/blob/master/documentation/design/test-module.md)\nfor this module.\n\n\n# Running tests\n\n@docs ReviewResult, run, runWithProjectData, runOnModules, runOnModulesWithProjectData\n\n\n# Making assertions\n\n@docs ExpectedError, expectNoErrors, expectErrors, error, atExactly, whenFixed, expectErrorsForModules, expectErrorsForElmJson, expectErrorsForReadme\n\n","unions":[{"name":"ExpectedError","comment":" An expectation for an error. Use [`error`](#error) to create one.\n","args":[],"cases":[]},{"name":"ReviewResult","comment":" The result of running a rule on a `String` containing source code.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"atExactly","comment":" Precise the exact position where the error should be shown to the user. This\nis only necessary when the `under` field is ambiguous.\n\n`atExactly` takes a record with start and end positions.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report multiple Debug.log calls\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"foo\" z\n b = Debug.log \"foo\" z\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }\n , Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }\n ]\n ]\n\nTip: By default, do not use this function. If the test fails because there is some\nambiguity, the test error will give you a recommendation of what to use as a parameter\nof `atExactly`, so you do not have to bother writing this hard-to-write argument yourself.\n\n","type":"{ start : { row : Basics.Int, column : Basics.Int }, end : { row : Basics.Int, column : Basics.Int } } -> Review.Test.ExpectedError -> Review.Test.ExpectedError"},{"name":"error","comment":" Create an expectation for an error.\n\n`message` should be the message you're expecting to be shown to the user.\n\n`under` is the part of the code where you are expecting the error to be shown to\nthe user. If it helps, imagine `under` to be the text under which the squiggly\nlines will appear if the error appeared in an editor.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\\\"\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\nIf there are multiple locations where the value of `under` appears, the test will\nfail unless you use [`atExactly`](#atExactly) to remove any ambiguity of where the\nerror should be used.\n\n","type":"{ message : String.String, details : List.List String.String, under : String.String } -> Review.Test.ExpectedError"},{"name":"expectErrors","comment":" Assert that the rule reported some errors, by specifying which ones.\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\nThe errors should be in the order of where they appear in the source code. An error\nat the start of the source code should appear earlier in the list than\nan error at the end of the source code.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\"\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForElmJson","comment":" Assert that the rule reported some errors for the `elm.json` file, by specifying which ones.\n\n test \"report an error when a module is unused\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectErrorsForElmJson\n [ Review.Test.error\n { message = \"Unused dependency `author/package`\"\n , details = [ \"Dependency should be removed\" ]\n , under = \"author/package\"\n }\n ]\n\nAlternatively, or if you need to specify errors for other files too, you can use [`expectErrorsForModules`](#expectErrorsForModules), specifying `elm.json` as the module name.\n\n sourceCode\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\", [ Review.Test.error someErrorModuleB ] )\n , ( \"elm.json\", [ Review.Test.error someErrorForElmJson ] )\n ]\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForModules","comment":" Assert that the rule reported some errors, by specifying which ones and the\nmodule for which they were reported.\n\nThis is the same as [`expectErrors`](#expectErrors), but for when you used\n[`runOnModules`](#runOnModules) or [`runOnModulesWithProjectData`](#runOnModulesWithProjectData).\nto create the test. When using those, the errors you expect need to be associated\nwith a module. If we don't specify this, your tests might pass because you\nexpected the right errors, but they may be reported for the wrong module!\n\nThe expected errors are tupled: the first element is the module name\n(for example: `List` or `My.Module.Name`) and the second element is the list of\nerrors you expect to be reported.\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\nThe errors should be in the order of where they appear in the source code. An error\nat the start of the source code should appear earlier in the list than\nan error at the end of the source code.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n all : Test\n all =\n test \"should report an error when a module uses `Debug.log`\" <|\n \\() ->\n [ \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\", \"\"\"\n module ModuleB exposing (a)\n a = Debug.log \"log\" 1\"\"\" ]\n |> Review.Test.runOnModules rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\"\n , [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n )\n ]\n\n","type":"List.List ( String.String, List.List Review.Test.ExpectedError ) -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForReadme","comment":" Assert that the rule reported some errors for the `README.md` file, by specifying which ones.\n\n test \"report an error when a module is unused\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addReadme { path = \"README.md\", context = \"# Project\\n...\" }\n in\n \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectErrorsForReadme\n [ Review.Test.error\n { message = \"Invalid link\"\n , details = [ \"README contains an invalid link\" ]\n , under = \"htt://example.com\"\n }\n ]\n\nAlternatively, or if you need to specify errors for other files too, you can use [`expectErrorsForModules`](#expectErrorsForModules), specifying `README.md` as the module name.\n\n sourceCode\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\", [ Review.Test.error someErrorModuleB ] )\n , ( \"README.md\", [ Review.Test.error someErrorForReadme ] )\n ]\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectNoErrors","comment":" Assert that the rule reported no errors. Note, this is equivalent to using [`expectErrors`](#expectErrors)\nlike `expectErrors []`.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should not report anything when \" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = foo n\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n ]\n\n","type":"Review.Test.ReviewResult -> Expect.Expectation"},{"name":"run","comment":" Run a `Rule` on a `String` containing source code. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrors`](#expectErrors) to assert\nthe errors reported by the rule.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n \"\"\"\n module SomeModule exposing (a)\n a = 1\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n\nThe source code needs to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a simpler version of [`runWithProjectData`](#runWithProjectData).\nIf your rule is interested in project related details, then you should use\n[`runWithProjectData`](#runWithProjectData) instead.\n\n","type":"Review.Rule.Rule -> String.String -> Review.Test.ReviewResult"},{"name":"runOnModules","comment":" Run a `Rule` on several modules. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrorsForModules`](#expectErrorsForModules) to assert\nthe errors reported by the rule.\n\nThis is the same as [`run`](#run), but you can pass several modules.\nThis is especially useful to test rules created with\n[`Review.Rule.newProjectRuleSchema`](./Review-Rule#newProjectRuleSchema), that look at\nseveral files, and where the context of the project is important.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n [ \"\"\"\n module A exposing (a)\n a = 1\"\"\", \"\"\"\n module B exposing (a)\n a = 1\"\"\" ]\n |> Review.Test.runOnModules rule\n |> Review.Test.expectNoErrors\n\nThe source codes need to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of each file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a simpler version of [`runOnModulesWithProjectData`](#runOnModulesWithProjectData).\nIf your rule is interested in project related details, then you should use\n[`runOnModulesWithProjectData`](#runOnModulesWithProjectData) instead.\n\n","type":"Review.Rule.Rule -> List.List String.String -> Review.Test.ReviewResult"},{"name":"runOnModulesWithProjectData","comment":" Run a `Rule` on several modules. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrorsForModules`](#expectErrorsForModules) to assert\nthe errors reported by the rule.\n\nThis is basically the same as [`run`](#run), but you can pass several modules.\nThis is especially useful to test rules created with\n[`Review.Rule.newProjectRuleSchema`](./Review-Rule#newProjectRuleSchema), that look at\nseveral modules, and where the context of the project is important.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n [ \"\"\"\n module A exposing (a)\n a = 1\"\"\", \"\"\"\n module B exposing (a)\n a = 1\"\"\" ]\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectNoErrors\n\nThe source codes need to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of each file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a more complex version of [`runOnModules`](#runOnModules). If your rule is not\ninterested in project related details, then you should use [`runOnModules`](#runOnModules) instead.\n\n","type":"Review.Project.Project -> Review.Rule.Rule -> List.List String.String -> Review.Test.ReviewResult"},{"name":"runWithProjectData","comment":" Run a `Rule` on a `String` containing source code, with data about the\nproject loaded, such as the contents of `elm.json` file.\n\n import My.Rule exposing (rule)\n import Review.Project as Project exposing (Project)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n \"\"\"module SomeModule exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectNoErrors\n\nThe source code needs to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a more complex version of [`run`](#run). If your rule is not\ninterested in project related details, then you should use [`run`](#run) instead.\n\n","type":"Review.Project.Project -> Review.Rule.Rule -> String.String -> Review.Test.ReviewResult"},{"name":"whenFixed","comment":" Create an expectation that the error provides an automatic fix, meaning that it used\nfunctions like [`errorWithFix`](./Review-Rule#errorWithFix), and an expectation of what the source\ncode should be after the error's fix have been applied.\n\nIn the absence of `whenFixed`, the test will fail if the error provides a fix.\nIn other words, you only need to use this function if the error provides a fix.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report multiple Debug.log calls\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = 1\n b = Debug.log \"foo\" 2\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.whenFixed \"\"\"module SomeModule exposing (b)\n a = 1\n b = 2\n \"\"\"\n ]\n ]\n\n","type":"String.String -> Review.Test.ExpectedError -> Review.Test.ExpectedError"}],"binops":[]}] \ No newline at end of file +[{"name":"Review.Fix","comment":" Tools to write automatic error fixes.\n\nWhen creating a [`Review.Rule.Error`](./Review-Rule#Error), you can provide an automatic\nfix for the error using [`Review.Rule.errorWithFix`](./Review-Rule#errorWithFix)\nor other functions that end with \"withFix\" so that the user doesn't need to fix\nthe problem themselves.\n\nIn the [CLI], the user can ask to fix the errors automatically, and in doing so,\nthey will be presented by a fix which they can accept or refuse. If the fix gets\nrefused, then the next fixable error will be presented. Otherwise, if the fix\ngets accepted, the file will be applied and the fixed file content get analyzed\nagain by the different rules in the user's configuration, and then another fix\nwill be presented. When there are no more fixable errors, the remaining errors\nwill be reported, just like when the user doesn't request errors to be automatically\nfixed.\n\nIn summary, errors will be presented one by one and the user will validate them.\nThe [CLI] also proposes an option to fix all the errors, which applies each fix\none by one and then asks the user to confirm the cumulated fix.\n\n\n# Guidelines\n\nAn automatic fix, when applied, should resolve the reported error completely.\nThis means that when the automatic fix is applied, the user should not have to\nthink about the error anymore or have to do additional work.\n\nImagine if the user applies a lot of automatic fixes all at once. We don't want them to have to\nremember having to do something, otherwise we may have just offloaded a lot of\nwork that they may forget to do. In that case, it is better not to provide a fix\nat all, so that they keep a reminder and the details of how to fix the problem.\n\nAn automatic fix should resolve only the reported error, not try to fix other\npotential errors. By only fixing one error at a time, the fix will be easier for\nthe user to digest and understand. The file will be re-reviewed when the fix is\napplied, and then another error can fix that one.\n\n\n# When (not) to provide an automatic fix?\n\nFor users, having an automatic fix always feels like a nice-to-have and they may\nrequest you to provide some, but they are not mandatory, and in some cases, it\nis better not to have any.\n\n\n## Reasons not to provide an automatic fix\n\n\n### A complete automatic fix is not possible\n\nSometimes, just by going through the whole file, you are missing some of the\ninformation needed to generate the right fix. Instead of providing a partial or\npotentially incorrect fix, it would be better to provide more details, hints or\nsuggestions.\n\n\n### The fix would result in a compiler error\n\nAn automatic fix should not cause changes that would break the file or the\nproject. In some cases, we can detect that the [fix will break things](#Problem),\nlike if the result of the fix is invalid Elm code (as in resulting in a parsing\nerror), but ultimately we can't detect that the project will still compile after\nthe fix is applied.\n\nUsers are notified that an automatic fix is available. For performance reasons,\nwe only check that a fix is valid before presenting it to the user and ignore it\nif it turns out to be invalid. This means that the user will be disappointed or\nconfused when the error ends up not being enforced. The only way we have to\nprevent this is to write tests, as fixes are applied in tests.\n\n\n### The user should learn about problem and how to solve it\n\nSometimes problems are learning opportunities, and it is worth having the user\nspend some time reading through the details of the error and trying several\nalternatives in order to understand the problem and the tradeoffs of the\nsolutions. You can guide them by using great error details!\n\n\n## Reasons to provide an automatic fix\n\nThe reasons to provide an automatic fix are basically the opposite of the\nreasons not to provide an automatic fix:\n\n - We know how to fix the problem completely and accurately\n - The task is menial and the user will not learn anything by fixing the error\n themselves\n\n\n# Strategies for writing automatic fixes effectively\n\n\n### Write a lot of tests\n\nAutomatic fixes are more error-prone than rules, especially since we may work\nwith re-writing ports of the code, for which the AST does not provide the\ncurrent formatting of a file (there is no information about spacing,\nline-breaks, ...). I suggest writing a lot of tests, and especially write tests\nwhere the formatting of the original file is a bit odd, as you may for instance\nunknowingly attempt to delete characters next to the thing you wanted to remove.\n\n\n### Store ranges in the context if necessary\n\nFixes work with ranges or position. If the context of a different element is not\navailable in the scope of where you create the error, then you should store it\nin the context of your rule.\n\n\n# Creating a fix\n\n@docs Fix, removeRange, replaceRangeBy, insertAt\n\n\n# Applying fixes\n\n@docs FixResult, Problem, fix\n\n[CLI]: https://github.com/jfmengels/node-elm-review\n\n\n# Tooling\n\n@docs toRecord\n\n","unions":[{"name":"FixResult","comment":" Represents the result of having applied a list of fixes to a source code.\n","args":[],"cases":[["Successful",["String.String"]],["Errored",["Review.Fix.Problem"]]]},{"name":"Problem","comment":" Represents a problem that may have occurred when attempting to apply a list\nof fixes.\n","args":[],"cases":[["Unchanged",[]],["SourceCodeIsNotValid",["String.String"]],["HasCollisionsInFixRanges",[]]]}],"aliases":[{"name":"Fix","comment":" Represents (part of a) fix that will be applied to a file's source code in order to\nautomatically fix a review error.\n","args":[],"type":"Review.Error.Fix"}],"values":[{"name":"fix","comment":" Apply the changes on the source code.\n","type":"Review.Error.Target -> List.List Review.Fix.Fix -> String.String -> Review.Fix.FixResult"},{"name":"insertAt","comment":" Insert some code at the given position.\n","type":"{ row : Basics.Int, column : Basics.Int } -> String.String -> Review.Fix.Fix"},{"name":"removeRange","comment":" Remove the code in between a range.\n","type":"Elm.Syntax.Range.Range -> Review.Fix.Fix"},{"name":"replaceRangeBy","comment":" Replace the code in between a range by some other code.\n","type":"Elm.Syntax.Range.Range -> String.String -> Review.Fix.Fix"},{"name":"toRecord","comment":" Describe a fix as a range to replace by something else.\n\nThis is meant for external tooling. You shouldn't have to care about this\n\n","type":"Review.Fix.Fix -> { range : Elm.Syntax.Range.Range, replacement : String.String }"}],"binops":[]},{"name":"Review.ModuleNameLookupTable","comment":" Looks up the name of the module a function or type comes from based on the position of the element in the module's AST.\n\nWhen encountering a `Expression.FunctionOrValue ModuleName String` (among other nodes where we refer to a function or value),\nthe module name available represents the module name that is in the source code. But that module name can be an alias to\na different import, or it can be empty, meaning that it refers to a local value or one that has been imported explicitly\nor implicitly. Resolving which module name the type or function can be a bit tricky sometimes, and I recommend against\ndoing it yourself.\n\n`elm-review` computes this for you already. Store this value inside your module context, then use\n[`ModuleNameLookupTable.moduleNameFor`](./Review-ModuleNameLookupTable#moduleNameFor) or\n[`ModuleNameLookupTable.moduleNameAt`](./Review-ModuleNameLookupTable#moduleNameAt) to get the name of the module the\ntype or value comes from.\n\n@docs ModuleNameLookupTable, moduleNameFor, moduleNameAt\n\nNote: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you should use this instead.\n\n","unions":[],"aliases":[{"name":"ModuleNameLookupTable","comment":" Associates positions in the AST of a module to the name of the module that the contained variable or type originates\nfrom.\n","args":[],"type":"Review.ModuleNameLookupTable.Internal.ModuleNameLookupTable"}],"values":[{"name":"moduleNameAt","comment":" Returns the name of the module the type or value referred to by this [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node).\n\nThe function returns `Just []` if the type or value was defined in this module. It returns `Just moduleName` if the Node is among these kinds of AST nodes (and `Nothing` for all the others):\n\n - Expression.FunctionOrValue\n - `nodeForTheName` in `Expression.RecordUpdateExpression nodeForTheName modifiers`\n - `nodeForTheName` in `TypeAnnotation.Typed nodeForTheName args`\n - `nodeForTheName` in `Pattern.NamedPattern nodeForTheName subPatterns`\n\n```elm\nexpressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\nexpressionVisitor node context =\n case Node.value node of\n Expression.RecordUpdateExpr (Node range name) _ ->\n case ModuleNameLookupTable.moduleNameAt context.lookupTable range of\n Just moduleName ->\n ( [], markVariableAsUsed ( moduleName, name ) context )\n\n Nothing ->\n ( [], context )\n\n _ ->\n ( [], context )\n```\n\nNote: If using a `Node` is easier in your situation than using a `Range`, use [`moduleNameFor`](#moduleNameFor) instead.\n\n","type":"Review.ModuleNameLookupTable.ModuleNameLookupTable -> Elm.Syntax.Range.Range -> Maybe.Maybe Elm.Syntax.ModuleName.ModuleName"},{"name":"moduleNameFor","comment":" Returns the name of the module the type or value referred to by this [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node).\n\nThe function returns `Just []` if the type or value was defined in this module. It returns `Just moduleName` if the Node is among these kinds of AST nodes (and `Nothing` for all the others):\n\n - Expression.FunctionOrValue\n - `nodeForTheName` in `Expression.RecordUpdateExpression nodeForTheName modifiers`\n - `nodeForTheName` in `TypeAnnotation.Typed nodeForTheName args`\n - `nodeForTheName` in `Pattern.NamedPattern nodeForTheName subPatterns`\n\n```elm\nexpressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\nexpressionVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue _ \"color\" ->\n if ModuleNameLookupTable.moduleNameFor context.lookupTable node == Just [ \"Css\" ] then\n ( [ Rule.error\n { message = \"Do not use `Css.color` directly, use the Colors module instead\"\n , details = [ \"We made a module which contains all the available colors of our design system. Use the functions in there instead.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n```\n\nNote: If using a `Range` is easier in your situation than using a `Node`, use [`moduleNameAt`](#moduleNameAt) instead.\n\n","type":"Review.ModuleNameLookupTable.ModuleNameLookupTable -> Elm.Syntax.Node.Node a -> Maybe.Maybe Elm.Syntax.ModuleName.ModuleName"}],"binops":[]},{"name":"Review.Project","comment":" Represents the contents of the project to be analyzed. This information will\nthen be fed to the review rules.\n\nYou may need to use use this module if you want\n\n - to create test cases where the project is in a certain configuration\n - to make `elm-review` run in a new environment\n\nYou can safely ignore this module if you just want to write a review rule that\ndoes not look at project information (like the `elm.json`, dependencies, ...).\n\n@docs Project, new\n\n\n## Elm modules\n\n@docs ProjectModule, addModule, addParsedModule, removeModule, modules, modulesThatFailedToParse, precomputeModuleGraph\n\n\n# `elm.json`\n\n@docs addElmJson, elmJson\n\n\n# `README.md`\n\n@docs addReadme, readme\n\n\n# Project dependencies\n\n@docs addDependency, removeDependencies, dependencies\n\n","unions":[],"aliases":[{"name":"Project","comment":" Holds all the information related to the project such as the contents of\nthe `elm.json` file, the project modules and the project dependencies.\n","args":[],"type":"Review.Project.Internal.Project"},{"name":"ProjectModule","comment":" Represents a parsed file.\n","args":[],"type":"Review.Project.Internal.ProjectModule"}],"values":[{"name":"addDependency","comment":" Add a dependency to the project. These will be available for rules to make\nbetter assumptions on what is happening in the code.\n\nKnowing the dependencies of the project will also help better parse the source\nfiles, since the dependencies will allow us to know the precedence and\nassociativity of operators, which has an impact on the resulting AST when\nparsing a file.\n\n","type":"Review.Project.Dependency.Dependency -> Review.Project.Project -> Review.Project.Project"},{"name":"addElmJson","comment":" Add the content of the `elm.json` file to the project, making it\navailable for rules to access using\n[`Review.Rule.withElmJsonModuleVisitor`](./Review-Rule#withElmJsonModuleVisitor) and\n[`Review.Rule.withElmJsonProjectVisitor`](./Review-Rule#withElmJsonProjectVisitor).\n\nThe `raw` value should be the raw JSON as a string, and `project` corresponds to\n[`elm/project-metadata-utils`'s `Elm.Project.Project` type](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project#Project).\n\n","type":"{ path : String.String, raw : String.String, project : Elm.Project.Project } -> Review.Project.Project -> Review.Project.Project"},{"name":"addModule","comment":" Add an Elm file to the project. If a file with the same path already exists,\nthen it will replace it.\n\nIf the file is syntactically valid Elm code, it will then be analyzed by the\nreview rules. Otherwise, the file will be added to the list of files that failed\nto parse, which you can get using [`modulesThatFailedToParse`](#modulesThatFailedToParse),\nand for which a parsing error will be reported when running [`Review.review`](./Review#review).\n\n","type":"{ path : String.String, source : String.String } -> Review.Project.Project -> Review.Project.Project"},{"name":"addParsedModule","comment":" Add an already parsed module to the project. This module will then be analyzed by the rules.\n","type":"{ path : String.String, source : String.String, ast : Elm.Syntax.File.File } -> Review.Project.Project -> Review.Project.Project"},{"name":"addReadme","comment":" Add the content of the `README.md` file to the project, making it\navailable for rules to access using\n[`Review.Rule.withReadmeModuleVisitor`](./Review-Rule#withReadmeModuleVisitor) and\n[`Review.Rule.withReadmeProjectVisitor`](./Review-Rule#withReadmeProjectVisitor).\n","type":"{ path : String.String, content : String.String } -> Review.Project.Project -> Review.Project.Project"},{"name":"dependencies","comment":" Get the [dependencies](./Review-Project-Dependency#Dependency) of the project.\n","type":"Review.Project.Project -> Dict.Dict String.String Review.Project.Dependency.Dependency"},{"name":"elmJson","comment":" Get the contents of the `elm.json` file, if available.\n\nThis will give you a `Elm.Project.Project` type from the\n[`elm/project-metadata-utils`](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.0/Elm-Project)\npackage, so you will need to install and use it to gain access to the\ninformation from the `elm.json` file.\n\n","type":"Review.Project.Project -> Maybe.Maybe { path : String.String, raw : String.String, project : Elm.Project.Project }"},{"name":"modules","comment":" Get the list of modules in the project.\n","type":"Review.Project.Project -> List.List Review.Project.ProjectModule"},{"name":"modulesThatFailedToParse","comment":" Get the list of file paths that failed to parse, because they were syntactically invalid Elm code.\n","type":"Review.Project.Project -> List.List { path : String.String, source : String.String }"},{"name":"new","comment":" Create a new Project.\n","type":"Review.Project.Project"},{"name":"precomputeModuleGraph","comment":" Precomputes the module graph that you get using [`moduleGraph`](#moduleGraph).\n\nThis is to avoid a potentially long computation at every review run. Once the graph\nis precomputed, it will be recomputed every time a module is changed, meaning\nyou won't need to reuse this call `precomputeModuleGraph` again.\n\nYou should use this function if and when you know you loaded all the files in\nthe project.\n\n","type":"Review.Project.Project -> Review.Project.Project"},{"name":"readme","comment":" Get the contents of the `README.md` file, if available.\n","type":"Review.Project.Project -> Maybe.Maybe { path : String.String, content : String.String }"},{"name":"removeDependencies","comment":" Remove all dependencies of a project. Use this to flush the dependencies of\na project when they are changed, before re-adding them.\n","type":"Review.Project.Project -> Review.Project.Project"},{"name":"removeModule","comment":" Remove a module from the project by its path.\n","type":"String.String -> Review.Project.Project -> Review.Project.Project"}],"binops":[]},{"name":"Review.Project.Dependency","comment":" Functions to create and read data about the project's dependencies.\n\nYou may need to use use this module if you want\n\n - to create test cases where the project has specific dependencies\n - to make `elm-review` run in a new environment\n\nYou can safely ignore this module if you just want to write a review rule that\ndoes not look at the project's dependencies.\n\n@docs Dependency\n\n\n# Access\n\n@docs name, elmJson, modules\n\n\n# Create\n\n@docs create\n\n","unions":[{"name":"Dependency","comment":" Represents a dependency of the project.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"create","comment":" Create a dependency.\n\nYou will need to use this if you try to make `elm-review` run in a new environment,\nbut you can safely ignore it if you just want to write a review rule or run it\nin existing environments like the CLI tool.\n\nYou could something like the following to create a test project in your tests.\n\n import Elm.Docs\n import Elm.Project\n import Elm.Type as Type\n import Json.Decode as Decode\n import Review.Project as Project exposing (Project)\n import Review.Project.Dependency as Dependency exposing (Dependency)\n\n testProject : Project\n testProject =\n Project.new\n |> Project.addDependency\n\n dependency : String -> Dependency\n dependency license =\n Dependency.create\n \"author/dependency\"\n (createElmJson rawElmJson)\n modules\n\n modules : List Elm.Docs.Module\n modules =\n [ { name = \"Foo\"\n , comment = \"\"\n , unions = []\n , aliases = []\n , values =\n [ { name = \"someFunction\"\n , comment = \"\"\n , tipe = Type.Lambda (Type.Var \"a\") (Type.Var \"b\")\n }\n ]\n , binops = []\n }\n ]\n\n createElmJson : String -> Elm.Project.Project\n createElmJson rawElmJson =\n case Decode.decodeString Elm.Project.decoder rawElmJson of\n Ok elmJson ->\n elmJson\n\n Err _ ->\n Debug.todo \"Invalid elm.json contents in tests\"\n\n rawElmJson : String\n rawElmJson =\n \"\"\"\n {\n \"type\": \"package\",\n \"name\": \"author/dependency\",\n \"summary\": \"Summary\",\n \"license\": \"MIT\",\n \"version\": \"1.0.0\",\n \"exposed-modules\": [\n \"Foo\"\n ],\n \"elm-version\": \"0.19.0 <= v < 0.20.0\",\n \"dependencies\": {\n \"elm/core\": \"1.0.0 <= v < 2.0.0\"\n },\n \"test-dependencies\": {}\n }\n \"\"\"\n\n","type":"String.String -> Elm.Project.Project -> List.List Elm.Docs.Module -> Review.Project.Dependency.Dependency"},{"name":"elmJson","comment":" Get the [contents of the project's `elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.1/Elm-Project#Project)\nfile.\n","type":"Review.Project.Dependency.Dependency -> Elm.Project.Project"},{"name":"modules","comment":" Get the list of [modules](https://package.elm-lang.org/packages/elm/project-metadata-utils/1.0.1/Elm-Docs#Module)\ncontained in the dependency.\n","type":"Review.Project.Dependency.Dependency -> List.List Elm.Docs.Module"},{"name":"name","comment":" Get the name of the dependency.\n","type":"Review.Project.Dependency.Dependency -> String.String"}],"binops":[]},{"name":"Review.Rule","comment":" This module contains functions that are used for writing rules.\n\n\n# How does it work?\n\n`elm-review` reads the modules, `elm.json`, dependencies and `README.md` from your project,\nand turns each module into an [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree),\na tree-like structure which represents your source code, using the\n[`elm-syntax` package](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/).\n\n`elm-review` then feeds all this data into `review rules`, that traverse them to report problems.\nThe way that review rules go through the data depends on whether it is a [module rule](#creating-a-module-rule) or a [project rule](#creating-a-project-rule).\n\n`elm-review` relies on the [`elm-syntax` package](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/),\nand all the node types you'll see will be coming from there. You are likely to\nneed to have the documentation for that package open when writing a rule.\n\nThere are plenty of examples in this documentation, and you can also look at the\nsource code of existing rules to better grasp how they work.\n\n**NOTE**: These examples are only here to showcase how to write rules and how a function can\nbe used. They are not necessarily good rules to enforce. See the\n[section on whether to write a rule](./#when-to-write-or-enable-a-rule) for more on that.\nEven if you think they are good ideas to enforce, they are often not complete, as there are other\npatterns you would want to forbid, but that are not handled by the example.\n\n\n# What makes a good rule\n\nApart from the rationale on [whether a rule should be written](./#when-to-write-or-enable-a-rule),\nhere are a few tips on what makes a rule helpful.\n\nA review rule is an automated communication tool which sends messages to\ndevelopers who have written patterns your rule wishes to prevent. As all\ncommunication, the message is important.\n\n\n## A good rule name\n\nThe name of the rule (`NoUnusedVariables`, `NoDebug`, ...) should try to convey\nreally quickly what kind of pattern we're dealing with. Ideally, a user who\nencounters this pattern for the first time could guess the problem just from the\nname. And a user who encountered it several times should know how to fix the\nproblem just from the name too.\n\nI recommend having the name of the module containing the rule be the same as the\nrule name. This will make it easier to find the module in the project or on\nthe packages website when trying to get more information.\n\n\n## A helpful error message and details\n\nThe error message should give more information about the problem. It is split\ninto two parts:\n\n - The `message`: A short sentence that describes the forbidden pattern. A user\n that has encountered this error multiple times should know exactly what to do.\n Example: \"Function `foo` is never used\". With this information, a user who\n knows the rule probably knows that a function needs to be removed from the\n source code, and also knows which one.\n - The `details`: All the additional information that can be useful to the\n user, such as the rationale behind forbidding the pattern, and suggestions\n for a solution or alternative.\n\nWhen writing the error message that the user will see, try to make them be as\nhelpful as the messages the compiler gives you when it encounters a problem.\n\n\n## The smallest section of code that makes sense\n\nWhen creating an error, you need to specify under which section of the code this\nmessage appears. This is where you would see squiggly lines in your editor when\nyou have review or compiler errors.\n\nTo make the error easier to spot, it is best to make this section as small as\npossible, as long as that makes sense. For instance, in a rule that would forbid\n`Debug.log`, you would the error to appear under `Debug.log`, not on the whole\nfunction which contains this piece of code.\n\n\n## Good rule documentation\n\nThe rule documentation should give the same information as what you would see in\nthe error message.\n\nIf published in a package, the rule documentation should explain when not to\nenable the rule in the user's review configuration. For instance, for a rule that\nmakes sure that a package is publishable by ensuring that all docs are valid,\nthe rule might say something along the lines of \"If you are writing an\napplication, then you should not use this rule\".\n\nAdditionally, it could give a few examples of patterns that will be reported and\nof patterns that will not be reported, so that users can have a better grasp of\nwhat to expect.\n\n\n# Strategies for writing rules effectively\n\n\n## Use Test-Driven Development\n\nThis package comes with [`Review.Test`](./Review-Test), which works with [`elm-test`](https://github.com/elm-explorations/test).\nI recommend reading through [`the strategies for effective testing`](./Review-Test#strategies-for-effective-testing) before\nstarting writing a rule.\n\n\n## Look at the documentation for [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/)\n\n`elm-review` is heavily dependent on the types that [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/)\nprovides. If you don't understand the AST it provides, you will have a hard time\nimplementing the rule you wish to create.\n\n\n# Writing a Rule\n\n@docs Rule\n\n\n## Creating a module rule\n\nA \"module rule\" looks at modules (i.e. files) one by one. When it finishes looking at a module and reporting errors,\nit forgets everything about the module it just analyzed before starting to look at a different module. You should create one of these if you\ndo not need to know the contents of a different module in the project, such as what functions are exposed.\nIf you do need that information, you should create a [project rule](#creating-a-project-rule).\n\nIf you are new to writing rules, I would recommend learning how to build a module rule first, as they are in practice a\nsimpler version of project rules.\n\nThe traversal of a module rule is the following:\n\n - Read project-related info (only collect data in the context in these steps)\n - The `elm.json` file, visited by [`withElmJsonModuleVisitor`](#withElmJsonModuleVisitor)\n - The `README.md` file, visited by [`withReadmeModuleVisitor`](#withReadmeModuleVisitor)\n - The definition for dependencies, visited by [`withDependenciesModuleVisitor`](#withDependenciesModuleVisitor)\n - Visit the Elm module (in the following order)\n - The module definition, visited by [`withSimpleModuleDefinitionVisitor`](#withSimpleModuleDefinitionVisitor) and [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor)\n - The module's list of comments, visited by [`withSimpleCommentsVisitor`](#withSimpleCommentsVisitor) and [`withCommentsVisitor`](#withCommentsVisitor)\n - Each import, visited by [`withSimpleImportVisitor`](#withSimpleImportVisitor) and [`withImportVisitor`](#withImportVisitor)\n - The list of declarations, visited by [`withDeclarationListVisitor`](#withDeclarationListVisitor)\n - Each declaration, visited by [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor), [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor).\n Before evaluating the next declaration, the expression contained in the declaration\n will be visited recursively by [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor), [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor)\n - A final evaluation is made when the module has fully been visited, using [`withFinalModuleEvaluation`](#withFinalModuleEvaluation)\n\nEvaluating/visiting a node means two things:\n\n - Detecting patterns and reporting errors\n - Collecting data in a \"context\" (called `moduleContext` for module rules) to have more information available in a later\n node evaluation. You can only use the context and update it with \"non-simple with\\*\" visitor functions.\n I recommend using the \"simple with\\*\" visitor functions if you don't need to do either, as they are simpler to use\n\n@docs ModuleRuleSchema, newModuleRuleSchema, fromModuleRuleSchema\n\n\n## Builder functions without context\n\n@docs withSimpleModuleDefinitionVisitor, withSimpleCommentsVisitor, withSimpleImportVisitor, withSimpleDeclarationVisitor, withSimpleExpressionVisitor\n\n\n## Builder functions with context\n\n@docs newModuleRuleSchemaUsingContextCreator\n@docs withModuleDefinitionVisitor\n@docs withCommentsVisitor\n@docs withImportVisitor\n@docs Direction, withDeclarationEnterVisitor, withDeclarationExitVisitor, withDeclarationVisitor, withDeclarationListVisitor\n@docs withExpressionEnterVisitor, withExpressionExitVisitor, withExpressionVisitor\n@docs withFinalModuleEvaluation\n\n\n## Builder functions to analyze the project's data\n\n@docs withElmJsonModuleVisitor, withReadmeModuleVisitor, withDependenciesModuleVisitor\n\n\n## Creating a project rule\n\nProject rules can look at the global picture of an Elm project. Contrary to module\nrules, which forget everything about the module they were looking at when going from\none module to another, project rules can retain information about previously\nanalyzed modules, and use it to report errors when analyzing a different module or\nafter all modules have been visited.\n\nProject rules can also report errors in the `elm.json` or the `README.md` files.\n\nIf you are new to writing rules, I would recommend learning [how to build a module rule](#creating-a-module-rule)\nfirst, as they are in practice a simpler version of project rules.\n\n@docs ProjectRuleSchema, newProjectRuleSchema, fromProjectRuleSchema, withModuleVisitor, withModuleContext, withModuleContextUsingContextCreator, withElmJsonProjectVisitor, withReadmeProjectVisitor, withDependenciesProjectVisitor, withFinalProjectEvaluation, withContextFromImportedModules\n\n\n## Requesting more information\n\n@docs ContextCreator, Metadata, initContextCreator, isInSourceDirectories, moduleNameFromMetadata, moduleNameNodeFromMetadata, withMetadata, withModuleNameLookupTable, withModuleKey\n\n\n## Errors\n\n@docs Error, error, errorWithFix, ModuleKey, errorForModule, errorForModuleWithFix, ElmJsonKey, errorForElmJson, ReadmeKey, errorForReadme, errorForReadmeWithFix\n@docs ReviewError, errorRuleName, errorMessage, errorDetails, errorRange, errorFixes, errorFilePath, errorTarget\n\n\n## Configuring exceptions\n\nThere are situations where you don't want review rules to report errors:\n\n1. You copied and updated over an external library because one of your needs wasn't met, and you don't want to modify it more than necessary.\n2. Your project contains generated source code, over which you have no control or for which you do not care that some rules are enforced (like the reports of unused variables).\n3. You want to introduce a rule progressively, because there are too many errors in the project for you to fix in one go. You can then ignore the parts of the project where the problem has not yet been solved, and fix them as you go.\n4. You wish to disable some rules for tests files (or enable some only for tests).\n\nYou can use the following functions to ignore errors in directories or files.\n\n**NOTE**: Even though they can be used to disable any errors, I **strongly recommend against**\ndoing so if you are not in the situations listed above. I highly recommend you\nleave a comment explaining the reason why you use these functions, or to\ncommunicate with your colleagues if you see them adding exceptions without\nreason or seemingly inappropriately.\n\n@docs ignoreErrorsForDirectories, ignoreErrorsForFiles\n\n\n# Running rules\n\n@docs review, reviewWithPrecollectionOfData, ruleName\n\n\n# Internals\n\n@docs Required, Forbidden\n\n","unions":[{"name":"ContextCreator","comment":" Create a context based on some other context.\nUse functions like [`withMetadata`](#withMetadata) to request more information.\n\n`from` will usually be a `projectContext` and `to` a `moduleContext`.\n\n","args":["from","to"],"cases":[]},{"name":"Direction","comment":" **DEPRECATED**\n\nThis is used in [`withDeclarationVisitor`](#withDeclarationVisitor) and [`withDeclarationVisitor`](#withDeclarationVisitor),\nwhich are deprecated and will be removed in the next major version. This type will be removed along with them.\n\nTo replicate the same behavior, take a look at\n\n - [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor).\n - [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor).\n\n**/DEPRECATED**\n\nRepresents whether a node is being traversed before having seen its children (`OnEnter`ing the node), or after (`OnExit`ing the node).\n\nWhen visiting the AST, declaration and expression nodes are visited twice: once\nwith `OnEnter`, before the children of the node are visited, and once with\n`OnExit`, after the children of the node have been visited.\nIn most cases, you'll only want to handle the `OnEnter` case, but there are cases\nwhere you'll want to visit a [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node)\nafter having seen its children.\n\nFor instance, if you are trying to detect the unused variables defined inside\nof a let expression, you will want to collect the declaration of variables,\nnote which ones are used, and at the end of the block report the ones that weren't used.\n\n expressionVisitor : Node Expression -> Direction -> Context -> ( List (Error {}), Context )\n expressionVisitor node direction context =\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Expression.FunctionOrValue moduleName name ) ->\n ( [], markVariableAsUsed context name )\n\n -- Find variables declared in let expression\n ( Rule.OnEnter, Expression.LetExpression letBlock ) ->\n ( [], registerVariables context letBlock )\n\n -- When exiting the let expression, report the variables that were not used.\n ( Rule.OnExit, Expression.LetExpression _ ) ->\n ( unusedVariables context |> List.map createError, context )\n\n _ ->\n ( [], context )\n\n","args":[],"cases":[["OnEnter",[]],["OnExit",[]]]},{"name":"ElmJsonKey","comment":" A key to be able to report an error for the `elm.json` file. You need this\nkey in order to use the [`errorForElmJson`](#errorForElmJson) function. This is\nto prevent creating errors for it if you have not visited it.\n\nYou can get a `ElmJsonKey` using the [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor) function.\n\n","args":[],"cases":[]},{"name":"Error","comment":" Represents an error found by a [`Rule`](#Rule). These are created by the\nrules.\n","args":["scope"],"cases":[]},{"name":"Forbidden","comment":" Used for phantom type constraints. You can safely ignore this type.\n","args":[],"cases":[]},{"name":"Metadata","comment":" Metadata for the module being visited.\n\nDo not store the metadata directly in your context. Prefer storing the indiviaual pieces of informa\n\n","args":[],"cases":[]},{"name":"ModuleKey","comment":" A key to be able to report an error for a specific module. You need such a\nkey in order to use the [`errorForModule`](#errorForModule) function. This is to\nprevent creating errors for modules you have not visited, or files that do not exist.\n\nYou can get a `ModuleKey` from the `fromProjectToModule` and `fromModuleToProject`\nfunctions that you define when using [`newProjectRuleSchema`](#newProjectRuleSchema).\n\n","args":[],"cases":[]},{"name":"ModuleRuleSchema","comment":" Represents a schema for a module [`Rule`](#Rule).\n\nStart by using [`newModuleRuleSchema`](#newModuleRuleSchema), then add visitors to look at the parts of the code you are interested in.\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebug\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n","args":["schemaState","moduleContext"],"cases":[]},{"name":"ProjectRuleSchema","comment":" Represents a schema for a project [`Rule`](#Rule).\n\nSee the documentation for [`newProjectRuleSchema`](#newProjectRuleSchema) for\nhow to create a project rule.\n\n","args":["schemaState","projectContext","moduleContext"],"cases":[]},{"name":"ReadmeKey","comment":" A key to be able to report an error for the `README.md` file. You need this\nkey in order to use the [`errorForReadme`](#errorForReadme) function. This is\nto prevent creating errors for it if you have not visited it.\n\nYou can get a `ReadmeKey` using the [`withReadmeProjectVisitor`](#withReadmeProjectVisitor) function.\n\n","args":[],"cases":[]},{"name":"Required","comment":" Used for phantom type constraints. You can safely ignore this type.\n","args":[],"cases":[]},{"name":"Rule","comment":" Represents a construct able to analyze a project and report\nunwanted patterns.\n\nYou can create [module rules](#creating-a-module-rule) or [project rules](#creating-a-project-rule).\n\n","args":[],"cases":[]}],"aliases":[{"name":"ReviewError","comment":" Represents an error found by a [`Rule`](#Rule). These are the ones that will\nbe reported to the user.\n\nIf you are building a [`Rule`](#Rule), you shouldn't have to use this.\n\n","args":[],"type":"Review.Error.ReviewError"}],"values":[{"name":"error","comment":" Create an [`Error`](#Error). Use it when you find a pattern that the rule should forbid.\n\nThe `message` and `details` represent the [message you want to display to the user].\nThe `details` is a list of paragraphs, and each item will be visually separated\nwhen shown to the user. The details may not be empty, and this will be enforced\nby the tests automatically.\n\n error : Node a -> Error\n error node =\n Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n\nThe [`Range`] corresponds to the location where the error should be shown, i.e. where to put the squiggly lines in an editor.\nIn most cases, you can get it using [`Node.range`].\n\n[message you want to display to the user]: #a-helpful-error-message-and-details\n\n[`Range`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range\n[`Node.range`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#range\n\n","type":"{ message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error {}"},{"name":"errorDetails","comment":" Get the error details of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> List.List String.String"},{"name":"errorFilePath","comment":" Get the file path of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorFixes","comment":" Get the automatic [`fixes`](./Review-Fix#Fix) of an [`Error`](#Error), if it\ndefined any.\n","type":"Review.Error.ReviewError -> Maybe.Maybe (List.List Review.Fix.Fix)"},{"name":"errorForElmJson","comment":" Create an [`Error`](#Error) for the `elm.json` file.\n\nYou will need an [`ElmJsonKey`](#ElmJsonKey), which you can get from the [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)\nfunction.\n\nThe second argument is a function that takes the `elm.json` content as a raw string,\nand returns the error details. Using the raw string, you should try and find the\nmost fitting [`Range`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range)\npossible for the error.\n\n","type":"Review.Rule.ElmJsonKey -> (String.String -> { message : String.String, details : List.List String.String, range : Elm.Syntax.Range.Range }) -> Review.Rule.Error scope"},{"name":"errorForModule","comment":" Just like [`error`](#error), create an [`Error`](#Error) but for a specific module, instead of the module that is being\nvisited.\n\nYou will need a [`ModuleKey`](#ModuleKey), which you can get from the `fromProjectToModule` and `fromModuleToProject`\nfunctions that you define when using [`newProjectRuleSchema`](#newProjectRuleSchema).\n\n","type":"Review.Rule.ModuleKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error scope"},{"name":"errorForModuleWithFix","comment":" Just like [`errorForModule`](#errorForModule), create an [`Error`](#Error) for a specific module, but\nprovides an automatic fix that the user can apply.\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`errorForModule`](#errorForModule) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"Review.Rule.ModuleKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error scope"},{"name":"errorForReadme","comment":" Create an [`Error`](#Error) for the `README.md` file.\n\nYou will need an [`ReadmeKey`](#ReadmeKey), which you can get from the [`withReadmeProjectVisitor`](#withReadmeProjectVisitor)\nfunction.\n\n","type":"Review.Rule.ReadmeKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> Review.Rule.Error scope"},{"name":"errorForReadmeWithFix","comment":" Just like [`errorForReadme`](#errorForReadme), create an [`Error`](#Error) for the `README.md` file, but\nprovides an automatic fix that the user can apply.\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`errorForReadme`](#errorForReadme) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"Review.Rule.ReadmeKey -> { message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error scope"},{"name":"errorMessage","comment":" Get the error message of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorRange","comment":" Get the [`Range`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Range)\nof an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> Elm.Syntax.Range.Range"},{"name":"errorRuleName","comment":" Get the name of the rule that triggered this [`Error`](#Error).\n","type":"Review.Error.ReviewError -> String.String"},{"name":"errorTarget","comment":" Get the file path of an [`Error`](#Error).\n","type":"Review.Error.ReviewError -> Review.Error.Target"},{"name":"errorWithFix","comment":" Creates an [`Error`](#Error), just like the [`error`](#error) function, but\nprovides an automatic fix that the user can apply.\n\n import Review.Fix as Fix\n\n error : Node a -> Error\n error node =\n Rule.errorWithFix\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n [ Fix.removeRange (Node.range node) ]\n\nTake a look at [`Review.Fix`](./Review-Fix) to know more on how to makes fixes.\n\nIf the list of fixes is empty, then it will give the same error as if you had\ncalled [`error`](#error) instead.\n\n**Note**: Each fix applies on a location in the code, defined by a range. To avoid an\nunpredictable result, those ranges may not overlap. The order of the fixes does\nnot matter.\n\n","type":"{ message : String.String, details : List.List String.String } -> Elm.Syntax.Range.Range -> List.List Review.Fix.Fix -> Review.Rule.Error {}"},{"name":"fromModuleRuleSchema","comment":" Create a [`Rule`](#Rule) from a configured [`ModuleRuleSchema`](#ModuleRuleSchema).\n","type":"Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext -> Review.Rule.Rule"},{"name":"fromProjectRuleSchema","comment":" Create a [`Rule`](#Rule) from a configured [`ProjectRuleSchema`](#ProjectRuleSchema).\n","type":"Review.Rule.ProjectRuleSchema { schemaState | withModuleContext : Review.Rule.Forbidden, hasAtLeastOneVisitor : () } projectContext moduleContext -> Review.Rule.Rule"},{"name":"ignoreErrorsForDirectories","comment":" Ignore the errors reported for modules in specific directories of the project.\n\nUse it when you don't want to get review errors for generated source code or for\nlibraries that you forked and copied over to your project.\n\n config : List Rule\n config =\n [ Some.Rule.rule\n |> Rule.ignoreErrorsForDirectories [ \"generated-source/\", \"vendor/\" ]\n , Some.Other.Rule.rule\n ]\n\nIf you want to ignore some directories for all of your rules, you can apply\n`ignoreErrorsForDirectories` like this:\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n |> List.map (Rule.ignoreErrorsForDirectories [ \"generated-source/\", \"vendor/\" ])\n\nThe paths should be relative to the `elm.json` file, just like the ones for the\n`elm.json`'s `source-directories`.\n\nYou can apply `ignoreErrorsForDirectories`several times for a rule, to add more\nignored directories.\n\nYou can also use it when writing a rule. We can hardcode in the rule that a rule\nis not applicable to a folder, like `tests/` for instance. The following example\nforbids using `Debug.todo` anywhere in the code, except in tests.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForDirectories [ \"tests/\" ]\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node =\n case Node.value node of\n Expression.FunctionOrValue [ \"Debug\" ] \"todo\" ->\n [ Rule.error\n { message = \"Remove the use of `Debug.todo` before shipping to production\"\n , details = [ \"`Debug.todo` is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\n","type":"List.List String.String -> Review.Rule.Rule -> Review.Rule.Rule"},{"name":"ignoreErrorsForFiles","comment":" Ignore the errors reported for specific file paths.\nUse it when you don't want to review generated source code or files from external\nsources that you copied over to your project and don't want to be touched.\n\n config : List Rule\n config =\n [ Some.Rule.rule\n |> Rule.ignoreErrorsForFiles [ \"src/Some/File.elm\" ]\n , Some.Other.Rule.rule\n ]\n\nIf you want to ignore some files for all of your rules, you can apply\n`ignoreErrorsForFiles` like this:\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n |> List.map (Rule.ignoreErrorsForFiles [ \"src/Some/File.elm\" ])\n\nThe paths should be relative to the `elm.json` file, just like the ones for the\n`elm.json`'s `source-directories`.\n\nYou can apply `ignoreErrorsForFiles` several times for a rule, to add more\nignored files.\n\nYou can also use it when writing a rule. We can simplify the example from [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor)\nby hardcoding an exception into the rule (that forbids the use of `Html.button` except in the \"Button\" module).\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoHtmlButton\"\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForFiles [ \"src/Button.elm\" ]\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue [ \"Html\" ] \"button\" ->\n [ Rule.error\n { message = \"Do not use `Html.button` directly\"\n , details = [ \"At fruits.com, we've built a nice `Button` module that suits our needs better. Using this module instead of `Html.button` ensures we have a consistent button experience across the website.\" ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\n","type":"List.List String.String -> Review.Rule.Rule -> Review.Rule.Rule"},{"name":"initContextCreator","comment":" Initialize a new context creator.\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { moduleName = Rule.moduleNameFromMetadata metadata\n\n -- ...other fields\n }\n )\n |> Rule.withMetadata\n\n","type":"(from -> to) -> Review.Rule.ContextCreator from to"},{"name":"isInSourceDirectories","comment":" Learn whether the current module is in the \"source-directories\" of the project. You can use this information to\nknow whether the module is part of the tests or of the production code.\n","type":"Review.Rule.Metadata -> Basics.Bool"},{"name":"moduleNameFromMetadata","comment":" Get the module name of the current module.\n","type":"Review.Rule.Metadata -> Elm.Syntax.ModuleName.ModuleName"},{"name":"moduleNameNodeFromMetadata","comment":" Get the [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node) to the module name of the current module.\n","type":"Review.Rule.Metadata -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName"},{"name":"newModuleRuleSchema","comment":" Creates a schema for a module rule. Will require adding module visitors\ncalling [`fromModuleRuleSchema`](#fromModuleRuleSchema) to create a usable\n[`Rule`](#Rule). Use \"with\\*\" functions from this module, like\n[`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) or [`withSimpleImportVisitor`](#withSimpleImportVisitor)\nto make it report something.\n\nThe first argument is the rule name. I _highly_ recommend naming it just like the\nmodule name (including all the `.` there may be).\n\nThe second argument is the initial `moduleContext`, i.e. the data that the rule will\naccumulate as the module will be traversed, and allows the rule to know/remember\nwhat happens in other parts of the module. If you don't need a context, I\nrecommend specifying `()`, and using functions from this module with names\nstarting with \"withSimple\".\n\n**NOTE**: Do not store functions, JSON values or regular expressions in your contexts, as they will be\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\n module My.Rule.Name exposing (rule)\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"My.Rule.Name\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\nIf you do need information from other parts of the module, then you should specify\nan initial context, and I recommend using \"with\\*\" functions without \"Simple\" in\ntheir name, like [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\n[`withImportVisitor`](#withImportVisitor) or [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUnusedVariables\" initialContext\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.withImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n type alias Context =\n { declaredVariables : List String\n , usedVariables : List String\n }\n\n initialContext : Context\n initialContext =\n { declaredVariables = [], usedVariables = [] }\n\n","type":"String.String -> moduleContext -> Review.Rule.ModuleRuleSchema { canCollectProjectData : () } moduleContext"},{"name":"newModuleRuleSchemaUsingContextCreator","comment":" Same as [`newModuleRuleSchema`](#newModuleRuleSchema), except that you can request for data to help initialize the context.\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\n module My.Rule.Name exposing (rule)\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"My.Rule.Name\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\nIf you do need information from other parts of the module, then you should specify\nan initial context, and I recommend using \"with\\*\" functions without \"Simple\" in\ntheir name, like [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\n[`withImportVisitor`](#withImportVisitor) or [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchemaUsingContextCreator \"Rule.Name\" contextCreator\n -- visitors\n |> Rule.fromModuleRuleSchema\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { hasTodoBeenImported = False\n , hasToStringBeenImported = False\n , isInSourceDirectories = Rule.isInSourceDirectories metadata\n }\n )\n |> Rule.withMetadata\n\n","type":"String.String -> Review.Rule.ContextCreator () moduleContext -> Review.Rule.ModuleRuleSchema {} moduleContext"},{"name":"newProjectRuleSchema","comment":" Creates a schema for a project rule. Will require adding project visitors and calling\n[`fromProjectRuleSchema`](#fromProjectRuleSchema) to create a usable [`Rule`](#Rule).\n\nThe first argument is the rule name. I _highly_ recommend naming it just like the\nmodule name (including all the `.` there may be).\n\nThe second argument is the initial `projectContext`, i.e. the data that the rule will\naccumulate as the project will be traversed, and allows the rule to know/remember\nwhat happens in other parts of the project.\n\n**NOTE**: Do not store functions, JSON values or regular expressions in your contexts, as they will be\ncompared internally, which [may cause Elm to crash](https://package.elm-lang.org/packages/elm/core/latest/Basics#(==)).\n\nProject rules traverse the project in the following order:\n\n - Read and/or report errors in project files\n - The `elm.json` file, visited by [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)\n - The `README.md` file, visited by [`withReadmeProjectVisitor`](#withReadmeProjectVisitor)\n - The definition for dependencies, visited by [`withDependenciesProjectVisitor`](#withDependenciesProjectVisitor)\n - The Elm modules one by one, visited by [`withModuleVisitor`](#withModuleVisitor),\n following the same traversal order as for module rules but without reading the project files (`elm.json`, ...).\n - A final evaluation when all modules have been visited, using [`withFinalProjectEvaluation`](#withFinalProjectEvaluation)\n\nEvaluating/visiting a node means two things:\n\n - Detecting patterns and reporting errors\n - Collecting data in a \"context\", which will be either a `projectContext` or a `moduleContext` depending on the part of the project being visited, to have more information available in a later\n part of the traversal evaluation.\n\n","type":"String.String -> projectContext -> Review.Rule.ProjectRuleSchema { canAddModuleVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"review","comment":" Review a project and gives back the errors raised by the given rules.\n\nNote that you won't need to use this function when writing a rule. You should\nonly need it if you try to make `elm-review` run in a new environment.\n\n import Review.Project as Project exposing (Project, ProjectModule)\n import Review.Rule as Rule exposing (Rule)\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n\n project : Project\n project =\n Project.new\n |> Project.addModule { path = \"src/A.elm\", source = \"module A exposing (a)\\na = 1\" }\n |> Project.addModule { path = \"src/B.elm\", source = \"module B exposing (b)\\nb = 1\" }\n\n doReview =\n let\n ( errors, rulesWithCachedValues ) =\n Rule.review rules project\n in\n doSomethingWithTheseValues\n\nThe resulting `List Rule` is the same list of rules given as input, but with an\nupdated internal cache to make it faster to re-run the rules on the same project.\nIf you plan on re-reviewing with the same rules and project, for instance to\nreview the project after a file has changed, you may want to store the rules in\nyour `Model`.\n\nThe rules are functions, so doing so will make your model unable to be\nexported/imported with `elm/browser`'s debugger, and may cause a crash if you try\nto compare them or the model that holds them.\n\n","type":"List.List Review.Rule.Rule -> Review.Project.Internal.Project -> ( List.List Review.Rule.ReviewError, List.List Review.Rule.Rule )"},{"name":"reviewWithPrecollectionOfData","comment":" Review a project and gives back the errors raised by the given rules.\n\nNote that you won't need to use this function when writing a rule. You should\nonly need it if you try to make `elm-review` run in a new environment.\n\n import Review.Project as Project exposing (Project, ProjectModule)\n import Review.Rule as Rule exposing (Rule)\n\n config : List Rule\n config =\n [ Some.Rule.rule\n , Some.Other.Rule.rule\n ]\n\n project : Project\n project =\n Project.new\n |> Project.addModule { path = \"src/A.elm\", source = \"module A exposing (a)\\na = 1\" }\n |> Project.addModule { path = \"src/B.elm\", source = \"module B exposing (b)\\nb = 1\" }\n\n doReview =\n let\n ( errors, rulesWithCachedValues ) =\n Rule.reviewWithPrecollectionOfData rules Nothing project\n in\n doSomethingWithTheseValues\n\nThe resulting `List Rule` is the same list of rules given as input, but with an\nupdated internal cache to make it faster to re-run the rules on the same project.\nIf you plan on re-reviewing with the same rules and project, for instance to\nreview the project after a file has changed, you may want to store the rules in\nyour `Model`.\n\nThe rules are functions, so doing so will make your model unable to be\nexported/imported with `elm/browser`'s debugger, and may cause a crash if you try\nto compare them or the model that holds them.\n\n","type":"List.List Review.Rule.Rule -> Maybe.Maybe Review.Rule.ProjectData -> Review.Project.Internal.Project -> { errors : List.List Review.Rule.ReviewError, rules : List.List Review.Rule.Rule, projectData : Maybe.Maybe Review.Rule.ProjectData }"},{"name":"ruleName","comment":" Get the name of a rule.\n\nYou should not have to use this when writing a rule.\n\n","type":"Review.Rule.Rule -> String.String"},{"name":"withCommentsVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's comments, collect data in\nthe `context` and/or report patterns.\n\nThis visitor will give you access to the list of all comments in the module all at once.\n\nTip: If you do not need to collect data in this visitor, you may wish to use the\nsimpler [`withSimpleCommentsVisitor`](#withSimpleCommentsVisitor) function.\n\n","type":"(List.List (Elm.Syntax.Node.Node String.String) -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withContextFromImportedModules","comment":" Allows the rule to have access to the context of the modules imported by the\ncurrently visited module. You can use for instance to know what is exposed in a\ndifferent module.\n\nWhen you finish analyzing a module, the `moduleContext` is turned into a `projectContext`\nthrough [`fromModuleToProject`](#newProjectRuleSchema). Before analyzing a module,\nthe `projectContext`s of its imported modules get folded into a single one\nstarting with the initial context (that may have visited the\n[`elm.json` file](#withElmJsonProjectVisitor) and/or the [project's dependencies](#withDependenciesProjectVisitor))\nusing [`foldProjectContexts`](#newProjectRuleSchema).\n\nIf there is information about another module that you wish to access, you should\ntherefore store it in the `moduleContext`, and have it persist when transitioning\nto a `projectContext` and back to a `moduleContext`.\n\nYou can only access data from imported modules, not from modules that import the\ncurrent module. If you need to do so, I suggest collecting all the information\nyou need, and re-evaluate if from [the final project evaluation function](#withFinalProjectEvaluation).\n\nIf you don't use this function, you will only be able to access the contents of\nthe initial context. The benefit is that when re-analyzing the project, after a\nfix or when a file was changed in watch mode, much less work will need to be done\nand the analysis will be much faster, because we know other files won't influence\nthe results of other modules' analysis.\n\n","type":"Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext"},{"name":"withDeclarationEnterVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nThe following example forbids exposing a function or a value without it having a\ntype annotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Exposing as Exposing\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type ExposedFunctions\n = All\n | OnlySome (List String)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingDocumentationForExposedFunctions\" (OnlySome [])\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withDeclarationEnterVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n moduleDefinitionVisitor node context =\n case Node.value node |> Module.exposingList of\n Exposing.All _ ->\n ( [], All )\n\n Exposing.Explicit exposedValues ->\n ( [], OnlySome (List.filterMap exposedFunctionName exposedValues) )\n\n exposedFunctionName : Node Exposing.TopLevelExpose -> Maybe String\n exposedFunctionName value =\n case Node.value value of\n Exposing.FunctionExpose functionName ->\n Just functionName\n\n _ ->\n Nothing\n\n declarationVisitor : Node Declaration -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n declarationVisitor node direction context =\n case Node.value node of\n Declaration.FunctionDeclaration { documentation, declaration } ->\n let\n functionName : String\n functionName =\n Node.value declaration |> .name |> Node.value\n in\n if documentation == Nothing && isExposed context functionName then\n ( [ Rule.error\n { message = \"Exposed function \" ++ functionName ++ \" is missing a type annotation\"\n , details =\n [ \"Type annotations are very helpful for people who use the module. It can give a lot of information without having to read the contents of the function.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n isExposed : ExposedFunctions -> String -> Bool\n isExposed exposedFunctions name =\n case exposedFunctions of\n All ->\n True\n\n OnlySome exposedList ->\n List.member name exposedList\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationExitVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nThe following example reports unused parameters from top-level declarations.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withDeclarationEnterVisitor declarationEnterVisitor\n |> Rule.withDeclarationExitVisitor declarationExitVisitor\n -- Omitted, but this marks parameters as used\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n declarationEnterVisitor : Node Declaration -> fContext -> ( List (Error {}), Context )\n declarationEnterVisitor node context =\n case Node.value node of\n Declaration.FunctionDeclaration function ->\n ( [], registerArguments context function )\n\n _ ->\n ( [], context )\n\n declarationExitVisitor : Node Declaration -> Context -> ( List (Error {}), Context )\n declarationExitVisitor node context =\n case Node.value node of\n -- When exiting the function expression, report the parameters that were not used.\n Declaration.FunctionDeclaration function ->\n ( unusedParameters context |> List.map createError, removeArguments context )\n\n _ ->\n ( [], context )\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationListVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns.\n\nIt is similar to [withDeclarationVisitor](#withDeclarationVisitor), but the\nvisitor used with this function is called before the visitor added with\n[withDeclarationVisitor](#withDeclarationVisitor). You can use this visitor in\norder to look ahead and add the module's types and variables into your context,\nbefore visiting the contents of the module using [withDeclarationVisitor](#withDeclarationVisitor)\nand [withExpressionEnterVisitor](#withExpressionEnterVisitor). Otherwise, using\n[withDeclarationVisitor](#withDeclarationVisitor) is probably a simpler choice.\n\n","type":"(List.List (Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration) -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDeclarationVisitor","comment":" **DEPRECATED**\n\nUse [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) and [`withDeclarationExitVisitor`](#withDeclarationExitVisitor) instead.\nIn the next major version, this function will be removed and [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor) will be renamed to `withDeclarationVisitor`.\n\n**/DEPRECATED**\n\nAdd a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`),\ncollect data and/or report patterns. The declarations will be visited in the order of their definition.\n\nContrary to [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor), the\nvisitor function will be called twice with different [`Direction`](#Direction)\nvalues. It will be visited with `OnEnter`, then the children will be visited,\nand then it will be visited again with `OnExit`. If you do not check the value of\nthe `Direction` parameter, you might end up with duplicate errors and/or an\nunexpected `moduleContext`. Read more about [`Direction` here](#Direction).\n\nThe following example forbids exposing a function or a value without it having a\ntype annotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Exposing as Exposing\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type ExposedFunctions\n = All\n | OnlySome (List String)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingDocumentationForExposedFunctions\" (OnlySome [])\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withDeclarationVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n moduleDefinitionVisitor node context =\n case Node.value node |> Module.exposingList of\n Exposing.All _ ->\n ( [], All )\n\n Exposing.Explicit exposedValues ->\n ( [], OnlySome (List.filterMap exposedFunctionName exposedValues) )\n\n exposedFunctionName : Node Exposing.TopLevelExpose -> Maybe String\n exposedFunctionName value =\n case Node.value value of\n Exposing.FunctionExpose functionName ->\n Just functionName\n\n _ ->\n Nothing\n\n declarationVisitor : Node Declaration -> Direction -> ExposedFunctions -> ( List (Error {}), ExposedFunctions )\n declarationVisitor node direction context =\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Declaration.FunctionDeclaration { documentation, declaration } ) ->\n let\n functionName : String\n functionName =\n Node.value declaration |> .name |> Node.value\n in\n if documentation == Nothing && isExposed context functionName then\n ( [ Rule.error\n { message = \"Exposed function \" ++ functionName ++ \" is missing a type annotation\"\n , details =\n [ \"Type annotations are very helpful for people who use the module. It can give a lot of information without having to read the contents of the function.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n isExposed : ExposedFunctions -> String -> Bool\n isExposed exposedFunctions name =\n case exposedFunctions of\n All ->\n True\n\n OnlySome exposedList ->\n List.member name exposedList\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleDeclarationVisitor`](#withSimpleDeclarationVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> Review.Rule.Direction -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withDependenciesModuleVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[dependencies](./Review-Project-Dependency).\n\nYou can use this look at the modules contained in dependencies, which can make the rule very precise when it targets\nspecific functions.\n\n","type":"(Dict.Dict String.String Review.Project.Dependency.Dependency -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withDependenciesProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[dependencies](./Review-Project-Dependency).\n\nIt works exactly like [`withDependenciesModuleVisitor`](#withDependenciesModuleVisitor). The visitor will be called before any\nmodule is evaluated.\n\n","type":"(Dict.Dict String.String Review.Project.Dependency.Dependency -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withElmJsonModuleVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the project's\n[`elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project) file.\n\nThe following example forbids exposing a module in an \"Internal\" directory in your `elm.json` file.\n\n import Elm.Module\n import Elm.Project\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n Maybe Elm.Project.Project\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"DoNoExposeInternalModules\" Nothing\n |> Rule.withElmJsonModuleVisitor elmJsonVisitor\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.fromModuleRuleSchema\n\n elmJsonVisitor : Maybe Elm.Project.Project -> Context -> Context\n elmJsonVisitor elmJson context =\n elmJson\n\n moduleDefinitionVisitor : Node Module -> Context -> ( List (Error {}), Context )\n moduleDefinitionVisitor node context =\n let\n moduleName : List String\n moduleName =\n Node.value node |> Module.moduleName\n in\n if List.member \"Internal\" moduleName then\n case context of\n Just (Elm.Project.Package { exposed }) ->\n let\n exposedModules : List String\n exposedModules =\n case exposed of\n Elm.Project.ExposedList names ->\n names\n |> List.map Elm.Module.toString\n\n Elm.Project.ExposedDict fakeDict ->\n fakeDict\n |> List.concatMap Tuple.second\n |> List.map Elm.Module.toString\n in\n if List.member (String.join \".\" moduleName) exposedModules then\n ( [ Rule.error \"Do not expose modules in `Internal` as part of the public API\" (Node.range node) ], context )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\n else\n ( [], context )\n\n","type":"(Maybe.Maybe Elm.Project.Project -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withElmJsonProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit the project's\n[`elm.json`](https://package.elm-lang.org/packages/elm/project-metadata-utils/latest/Elm-Project) file.\n\nIt works exactly like [`withElmJsonModuleVisitor`](#withElmJsonModuleVisitor).\nThe visitor will be called before any module is evaluated.\n\n","type":"(Maybe.Maybe { elmJsonKey : Review.Rule.ElmJsonKey, project : Elm.Project.Project } -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withExpressionEnterVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withExpressionVisitor`](#withExpressionVisitor), the\nvisitor function will be called only once, when the expression is \"entered\",\nmeaning before its children are visited.\n\nThe following example forbids the use of `Debug.log` even when it is imported like\n`import Debug exposing (log)`.\n\n import Elm.Syntax.Exposing as Exposing exposing (TopLevelExpose)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = DebugLogWasNotImported\n | DebugLogWasImported\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withImportVisitor importVisitor\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case ( Node.value node |> .moduleName |> Node.value, (Node.value node).exposingList |> Maybe.map Node.value ) of\n ( [ \"Debug\" ], Just (Exposing.All _) ) ->\n ( [], DebugLogWasImported )\n\n ( [ \"Debug\" ], Just (Exposing.Explicit exposedFunctions) ) ->\n let\n isLogFunction : Node Exposing.TopLevelExpose -> Bool\n isLogFunction exposeNode =\n case Node.value exposeNode of\n Exposing.FunctionExpose \"log\" ->\n True\n\n _ ->\n False\n in\n if List.any isLogFunction exposedFunctions then\n ( [], DebugLogWasImported )\n\n else\n ( [], DebugLogWasNotImported )\n\n _ ->\n ( [], DebugLogWasNotImported )\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case context of\n DebugLogWasNotImported ->\n ( [], context )\n\n DebugLogWasImported ->\n case Node.value node of\n Expression.FunctionOrValue [] \"log\" ->\n ( [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withExpressionExitVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withExpressionEnterVisitor`](#withExpressionEnterVisitor), the\nvisitor function will be called when the expression is \"exited\",\nmeaning after its children are visited.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withExpressionEnterVisitor expressionEnterVisitor\n |> Rule.withExpressionExitVisitor expressionExitVisitor\n |> Rule.fromModuleRuleSchema\n\n expressionEnterVisitor : Node Expression -> fContext -> ( List (Error {}), Context )\n expressionEnterVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue moduleName name ->\n ( [], markVariableAsUsed context name )\n\n -- Find variables declared in let expression\n Expression.LetExpression letBlock ->\n ( [], registerVariables context letBlock )\n\n _ ->\n ( [], context )\n\n expressionExitVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionExitVisitor node context =\n case Node.value node of\n -- When exiting the let expression, report the variables that were not used.\n Expression.LetExpression _ ->\n ( unusedVariables context |> List.map createError, removeVariables context )\n\n _ ->\n ( [], context )\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withExpressionVisitor","comment":" **DEPRECATED**\n\nUse [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) and [`withExpressionExitVisitor`](#withExpressionExitVisitor) instead.\nIn the next major version, this function will be removed and [`withExpressionEnterVisitor`](#withExpressionEnterVisitor) will be renamed to `withExpressionVisitor`.\n\n**/DEPRECATED**\n\nAdd a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`), collect data in the `context` and/or report patterns.\nThe expressions are visited in pre-order depth-first search, meaning that an\nexpression will be visited, then its first child, the first child's children\n(and so on), then the second child (and so on).\n\nContrary to [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor), the\nvisitor function will be called twice with different [`Direction`](#Direction)\nvalues. It will be visited with `OnEnter`, then the children will be visited,\nand then it will be visited again with `OnExit`. If you do not check the value of\nthe `Direction` parameter, you might end up with duplicate errors and/or an\nunexpected `moduleContext`. Read more about [`Direction` here](#Direction).\n\nThe following example forbids the use of `Debug.log` even when it is imported like\n`import Debug exposing (log)`.\n\n import Elm.Syntax.Exposing as Exposing exposing (TopLevelExpose)\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = DebugLogWasNotImported\n | DebugLogWasImported\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebugEvenIfImported\" DebugLogWasNotImported\n |> Rule.withImportVisitor importVisitor\n |> Rule.withExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case ( Node.value node |> .moduleName |> Node.value, (Node.value node).exposingList |> Maybe.map Node.value ) of\n ( [ \"Debug\" ], Just (Exposing.All _) ) ->\n ( [], DebugLogWasImported )\n\n ( [ \"Debug\" ], Just (Exposing.Explicit exposedFunctions) ) ->\n let\n isLogFunction : Node Exposing.TopLevelExpose -> Bool\n isLogFunction exposeNode =\n case Node.value exposeNode of\n Exposing.FunctionExpose \"log\" ->\n True\n\n _ ->\n False\n in\n if List.any isLogFunction exposedFunctions then\n ( [], DebugLogWasImported )\n\n else\n ( [], DebugLogWasNotImported )\n\n _ ->\n ( [], DebugLogWasNotImported )\n\n expressionVisitor : Node Expression -> Direction -> Context -> ( List (Error {}), Context )\n expressionVisitor node direction context =\n case context of\n DebugLogWasNotImported ->\n ( [], context )\n\n DebugLogWasImported ->\n case ( direction, Node.value node ) of\n ( Rule.OnEnter, Expression.FunctionOrValue [] \"log\" ) ->\n ( [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleExpressionVisitor`](#withSimpleExpressionVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> Review.Rule.Direction -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withFinalModuleEvaluation","comment":" Add a function that makes a final evaluation of the module based only on the\ndata that was collected in the `moduleContext`. This can be useful if you can't or if\nit is hard to determine something as you traverse the module.\n\nThe following example forbids importing both `Element` (`elm-ui`) and\n`Html.Styled` (`elm-css`). Note that this is the same one written in the example\nfor [`withImportVisitor`](#withImportVisitor), but using [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n import Dict as Dict exposing (Dict)\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Elm.Syntax.Range exposing (Range)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n Dict (List String) Range\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUsingBothHtmlAndHtmlStyled\" Dict.empty\n |> Rule.withImportVisitor importVisitor\n |> Rule.withFinalModuleEvaluation finalEvaluation\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n ( [], Dict.insert (Node.value node |> .moduleName |> Node.value) (Node.range node) context )\n\n finalEvaluation : Context -> List (Error {})\n finalEvaluation context =\n case ( Dict.get [ \"Element\" ] context, Dict.get [ \"Html\", \"Styled\" ] context ) of\n ( Just elmUiRange, Just _ ) ->\n [ Rule.error\n { message = \"Do not use both `elm-ui` and `elm-css`\"\n , details = [ \"At fruits.com, we use `elm-ui` in the dashboard application, and `elm-css` in the rest of the code. We want to use `elm-ui` in our new projects, but in projects using `elm-css`, we don't want to use both libraries to keep things simple.\" ]\n }\n elmUiRange\n ]\n\n _ ->\n []\n\n","type":"(moduleContext -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withFinalProjectEvaluation","comment":" Add a function that makes a final evaluation of the project based only on the\ndata that was collected in the `projectContext`. This can be useful if you can't report something until you have visited\nall the modules in the project.\n\nIt works similarly [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\n**NOTE**: Do not create errors using the [`error`](#error) function using `withFinalProjectEvaluation`, but using [`errorForModule`](#errorForModule)\ninstead. When the project is evaluated in this function, you are not in the \"context\" of an Elm module (the idiomatic \"context\", not `projectContext` or `moduleContext`).\nThat means that if you call [`error`](#error), we won't know which module to associate the error to.\n\n","type":"(projectContext -> List.List (Review.Rule.Error { useErrorForModule : () })) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext"},{"name":"withImportVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[import statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Import)\n(`import Html as H exposing (div)`) in order of their definition, collect data\nin the `context` and/or report patterns.\n\nThe following example forbids importing both `Element` (`elm-ui`) and\n`Html.Styled` (`elm-css`).\n\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n type alias Context =\n { elmUiWasImported : Bool\n , elmCssWasImported : Bool\n }\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUsingBothHtmlAndHtmlStyled\" initialContext\n |> Rule.withImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n initialContext : Context\n initialContext =\n { elmUiWasImported = False\n , elmCssWasImported = False\n }\n\n error : Node Import -> Error {}\n error node =\n Rule.error\n { message = \"Do not use both `elm-ui` and `elm-css`\"\n , details = [ \"At fruits.com, we use `elm-ui` in the dashboard application, and `elm-css` in the rest of the code. We want to use `elm-ui` in our new projects, but in projects using `elm-css`, we don't want to use both libraries to keep things simple.\" ]\n }\n (Node.range node)\n\n importVisitor : Node Import -> Context -> ( List (Error {}), Context )\n importVisitor node context =\n case Node.value node |> .moduleName |> Node.value of\n [ \"Element\" ] ->\n if context.elmCssWasImported then\n ( [ error node ]\n , { context | elmUiWasImported = True }\n )\n\n else\n ( [ error node ]\n , { context | elmUiWasImported = True }\n )\n\n [ \"Html\", \"Styled\" ] ->\n if context.elmUiWasImported then\n ( [ error node ]\n , { context | elmCssWasImported = True }\n )\n\n else\n ( [ error node ]\n , { context | elmCssWasImported = True }\n )\n\n _ ->\n ( [], context )\n\nThis example was written in a different way in the example for [`withFinalModuleEvaluation`](#withFinalModuleEvaluation).\n\nTip: If you do not need to collect or use the `context` in this visitor, you may wish to use the\nsimpler [`withSimpleImportVisitor`](#withSimpleImportVisitor) function.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Import.Import -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withMetadata","comment":" Request metadata about the module.\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\metadata () ->\n { moduleName = Rule.moduleNameFromMetadata metadata\n , moduleNameNode = Rule.moduleNameNodeFromMetadata metadata\n , isInSourceDirectories = Rule.isInSourceDirectories metadata\n\n -- ...other fields\n }\n )\n |> Rule.withMetadata\n\n","type":"Review.Rule.ContextCreator Review.Rule.Metadata (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleContext","comment":" Specify, if the project rule has a [module visitor](#withModuleVisitor), how to:\n\n - convert a project context to a module context, through [`fromProjectToModule`]\n - convert a module context to a project context, through [`fromModuleToProject`]\n - fold (merge) project contexts, through [`foldProjectContexts`]\n\n**NOTE**: I suggest reading the section about [`foldProjectContexts`] carefully,\nas it is one whose implementation you will need to do carefully.\n\nIn project rules, we separate the context related to the analysis of the project\nas a whole and the context related to the analysis of a single module into a\n`projectContext` and a `moduleContext` respectively. We do this because in most\nproject rules you won't need all the data from the `projectContext` to analyze a\nmodule, and some data from the module context will not make sense inside the\nproject context.\n\nWhen visiting modules, `elm-review` follows a kind of map-reduce architecture.\nThe idea is the following: it starts with an initial `projectContext` and collects data\nfrom project-related files into it. Then, it visits every module with an initial\n`moduleContext` derived from a `projectContext`. At the end of a module's visit,\nthe final `moduleContext` will be transformed (\"map\") to a `projectContext`.\nAll or some of the `projectContext`s will then be folded into a single one,\nbefore being used in the [final project evaluation] or to compute another module's\ninitial `moduleContext`.\n\nThis will help make the result of the review as consistent as possible, by\nhaving the results be independent of the order the modules are visited. This also\ngives internal guarantees as to what needs to be re-computed when re-analyzing\nthe project, which leads to huge performance boosts in watch mode or after fixes\nhave been applied.\n\nThe following sections will explain each function, and will be summarized by an\nexample.\n\n\n### `fromProjectToModule`\n\nThe initial `moduleContext` of the module visitor is computed using `fromProjectToModule`\nfrom a `projectContext`. By default, this `projectContext` will be the result of\nvisiting the project-related files (`elm.json`, `README.md`, ...).\nIf [`withContextFromImportedModules`] was used, then the value will be this last\n`projectContext`, folded with each imported module's resulting `projectContext`,\nusing [`foldProjectContexts`].\n\nThe [`ModuleKey`] will allow you to report errors for this specific module\nusing [`errorForModule`](#errorForModule) from the [final project evaluation] or\nwhile visiting another module. If you plan to do that, you should store this in\nthe `moduleContext`. You can also get it from [`fromModuleToProject`], so choose\nwhat's most convenient.\n\nThe [`Node`] containing the module name is passed for convenience, so you don't\nhave to visit the module definition just to get the module name. Just like what\nit is in [`elm-syntax`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-ModuleName),\nthe value will be `[ \"My\", \"Module\" ]` if the module name is `My.Module`.\n\n\n### `fromModuleToProject`\n\nWhen a module has finished being analyzed, the final `moduleContext` will be\nconverted into a `projectContext`, so that it can later be folded with the other\nproject contexts using `foldProjectContexts`. The resulting `projectContext`\nwill be fed into the [final project evaluation] and potentially into\n[`fromProjectToModule`] for modules that import the current one.\n\nSimilarly to `fromProjectToModule`, the [`Node`] containing the module name and\nthe [`ModuleKey`] are passed for convenience, so you don't have to store them in\nthe `moduleContext` only to store them in the `projectContext`.\n\n\n### `foldProjectContexts`\n\nThis function folds two `projectContext` into one. This function requires a few\ntraits to always be true.\n\n - `projectContext`s should be \"merged\" together, not \"subtracted\". If for instance\n you want to detect the unused exports of a module, do not remove a declared\n export when you have found it used. Instead, store and accumulate the declared\n and used functions (both probably as `Set`s or `Dict`s), and in the final evaluation,\n filter out the declared functions if they are in the set of used functions.\n - The order of folding should not matter: `foldProjectContexts b (foldProjectContexts a initial)`\n should equal `foldProjectContexts a (foldProjectContexts b initial)`.\n [`List.concat`](https://package.elm-lang.org/packages/elm/core/latest/List#concat).\n - Folding an element twice into another should give the same result as folding\n it once. In other words, `foldProjectContexts a (foldProjectContexts a initial)`\n should equal `foldProjectContexts a initial`. You will likely need to use functions\n like [`Set.union`](https://package.elm-lang.org/packages/elm/core/latest/Set#union)\n and [`Dict.union`](https://package.elm-lang.org/packages/elm/core/latest/Dict#union)\n over addition and functions like\n [`List.concat`](https://package.elm-lang.org/packages/elm/core/latest/List#concat).\n\nIt is not necessary for the function to be commutative (i.e. that\n`foldProjectContexts a b` equals `foldProjectContexts b a`). It is fine to take\nthe value from the \"initial\" `projectContext` and ignore the other one, especially\nfor data computed in the project-related visitors (for which you will probably\ndefine a dummy value in the `fromModuleToProject` function). If it helps, imagine\nthat the second argument is the initial `projectContext`, or that it is an accumulator\njust like in `List.foldl`.\n\n\n### Summary example - Reporting unused exported functions\n\nAs an example, we will write a rule that reports functions that get exported\nbut are unused in the rest of the project.\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newProjectRuleSchema \"NoUnusedExportedFunctions\" initialProjectContext\n -- Omitted, but this will collect the list of exposed modules for packages.\n -- We don't want to report functions that are exposed\n |> Rule.withElmJsonProjectVisitor elmJsonVisitor\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContext\n { fromProjectToModule = fromProjectToModule\n , fromModuleToProject = fromModuleToProject\n , foldProjectContexts = foldProjectContexts\n }\n |> Rule.withFinalProjectEvaluation finalEvaluationForProject\n |> Rule.fromProjectRuleSchema\n\n moduleVisitor :\n Rule.ModuleRuleSchema {} ModuleContext\n -> Rule.ModuleRuleSchema { hasAtLeastOneVisitor : () } ModuleContext\n moduleVisitor schema =\n schema\n -- Omitted, but this will collect the exposed functions\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n -- Omitted, but this will collect uses of exported functions\n |> Rule.withExpressionEnterVisitor expressionVisitor\n\n type alias ProjectContext =\n { -- Modules exposed by the package, that we should not report\n exposedModules : Set ModuleName\n , exposedFunctions :\n -- An entry for each module\n Dict ModuleName\n { -- To report errors in this module\n moduleKey : Rule.ModuleKey\n\n -- An entry for each function with its location\n , exposed : Dict String Range\n }\n , used : Set ( ModuleName, String )\n }\n\n type alias ModuleContext =\n { isExposed : Bool\n , exposed : Dict String Range\n , used : Set ( ModuleName, String )\n }\n\n initialProjectContext : ProjectContext\n initialProjectContext =\n { exposedModules = Set.empty\n , modules = Dict.empty\n , used = Set.empty\n }\n\n fromProjectToModule : Rule.ModuleKey -> Node ModuleName -> ProjectContext -> ModuleContext\n fromProjectToModule moduleKey moduleName projectContext =\n { isExposed = Set.member (Node.value moduleName) projectContext.exposedModules\n , exposed = Dict.empty\n , used = Set.empty\n }\n\n fromModuleToProject : Rule.ModuleKey -> Node ModuleName -> ModuleContext -> ProjectContext\n fromModuleToProject moduleKey moduleName moduleContext =\n { -- We don't care about this value, we'll take\n -- the one from the initial context when folding\n exposedModules = Set.empty\n , exposedFunctions =\n if moduleContext.isExposed then\n -- If the module is exposed, don't collect the exported functions\n Dict.empty\n\n else\n -- Create a dictionary with all the exposed functions, associated to\n -- the module that was just visited\n Dict.singleton\n (Node.value moduleName)\n { moduleKey = moduleKey\n , exposed = moduleContext.exposed\n }\n , used = moduleContext.used\n }\n\n foldProjectContexts : ProjectContext -> ProjectContext -> ProjectContext\n foldProjectContexts newContext previousContext =\n { -- Always take the one from the \"initial\" context,\n -- which is always the second argument\n exposedModules = previousContext.exposedModules\n\n -- Collect the exposed functions from the new context and the previous one.\n -- We could use `Dict.merge`, but in this case, that doesn't change anything\n , exposedFunctions = Dict.union previousContext.modules newContext.modules\n\n -- Collect the used functions from the new context and the previous one\n , used = Set.union newContext.used previousContext.used\n }\n\n finalEvaluationForProject : ProjectContext -> List (Error { useErrorForModule : () })\n finalEvaluationForProject projectContext =\n -- Implementation of `unusedFunctions` omitted, but it returns the list\n -- of unused functions, along with the associated module key and range\n unusedFunctions projectContext\n |> List.map\n (\\{ moduleKey, functionName, range } ->\n Rule.errorForModule moduleKey\n { message = \"Function `\" ++ functionName ++ \"` is never used\"\n , details = [ \"\" ]\n }\n range\n )\n\n[`ModuleKey`]: #ModuleKey\n[`Node`]: https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node\n[`fromProjectToModule`]: #-fromprojecttomodule-\n[`fromModuleToProject`]: #-frommoduletoproject-\n[`foldProjectContexts`]: #-foldprojectcontexts-\n[final project evaluation]: #withFinalProjectEvaluation\n[`withContextFromImportedModules`]: #withContextFromImportedModules\n\n","type":"{ fromProjectToModule : Review.Rule.ModuleKey -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName -> projectContext -> moduleContext, fromModuleToProject : Review.Rule.ModuleKey -> Elm.Syntax.Node.Node Elm.Syntax.ModuleName.ModuleName -> moduleContext -> projectContext, foldProjectContexts : projectContext -> projectContext -> projectContext } -> Review.Rule.ProjectRuleSchema { schemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"withModuleContextUsingContextCreator","comment":" Use a [`ContextCreator`](#ContextCreator) to initialize your `moduleContext` and `projectContext`. This will allow\nyou to request more information\n\n import Review.Rule as Rule exposing (Rule)\n\n rule : Rule\n rule =\n Rule.newProjectRuleSchema \"NoMissingSubscriptionsCall\" initialProjectContext\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContextUsingContextCreator\n { fromProjectToModule = Rule.initContextCreator fromProjectToModule\n , fromModuleToProject =\n Rule.initContextCreator fromModuleToProject\n |> Rule.withModuleKey\n |> Rule.withMetadata\n , foldProjectContexts = foldProjectContexts\n }\n |> Rule.fromProjectRuleSchema\n\n fromProjectToModule : ProjectContext -> ModuleContext\n fromProjectToModule projectContext =\n { -- something\n }\n\n fromModuleToProject : Rule.ModuleKey -> Metadata -> ModuleContext -> ProjectContext\n fromModuleToProject moduleKey Metadata moduleContext =\n { moduleKeys = Dict.singleton (Rule.moduleNameFromMetadata metadata) moduleKey\n }\n\n","type":"{ fromProjectToModule : Review.Rule.ContextCreator projectContext moduleContext, fromModuleToProject : Review.Rule.ContextCreator moduleContext projectContext, foldProjectContexts : projectContext -> projectContext -> projectContext } -> Review.Rule.ProjectRuleSchema { schemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : (), withModuleContext : Review.Rule.Forbidden } projectContext moduleContext"},{"name":"withModuleDefinitionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[module definition](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Module) (`module SomeModuleName exposing (a, b)`), collect data in the `context` and/or report patterns.\n\nThe following example forbids the use of `Html.button` except in the \"Button\" module.\nThe example is simplified to only forbid the use of the `Html.button` expression.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Direction, Error, Rule)\n\n type Context\n = HtmlButtonIsAllowed\n | HtmlButtonIsForbidden\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoHtmlButton\" HtmlButtonIsForbidden\n |> Rule.withModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> Context -> ( List (Error {}), Context )\n moduleDefinitionVisitor node context =\n if (Node.value node |> Module.moduleName) == [ \"Button\" ] then\n ( [], HtmlButtonIsAllowed )\n\n else\n ( [], HtmlButtonIsForbidden )\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case context of\n HtmlButtonIsAllowed ->\n ( [], context )\n\n HtmlButtonIsForbidden ->\n case Node.value node of\n Expression.FunctionOrValue [ \"Html\" ] \"button\" ->\n ( [ Rule.error\n { message = \"Do not use `Html.button` directly\"\n , details = [ \"At fruits.com, we've built a nice `Button` module that suits our needs better. Using this module instead of `Html.button` ensures we have a consistent button experience across the website.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n _ ->\n ( [], context )\n\n _ ->\n ( [], context )\n\nTip: If you do not need to collect data in this visitor, you may wish to use the\nsimpler [`withSimpleModuleDefinitionVisitor`](#withSimpleModuleDefinitionVisitor) function.\n\nTip: The rule above is very brittle. What if `button` was imported using `import Html exposing (button)` or `import Html exposing (..)`, or if `Html` was aliased (`import Html as H`)? Then the rule above would\nnot catch and report the use `Html.button`. To handle this, check out [`withModuleNameLookupTable`](#withModuleNameLookupTable).\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Module.Module -> moduleContext -> ( List.List (Review.Rule.Error {}), moduleContext )) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withModuleKey","comment":" Request the [module key](ModuleKey) for this module.\n\n rule =\n Rule.newProjectRuleSchema \"NoMissingSubscriptionsCall\" initialProjectContext\n |> Scope.addProjectVisitors\n |> Rule.withModuleVisitor moduleVisitor\n |> Rule.withModuleContextUsingContextCreator\n { fromProjectToModule = Rule.initContextCreator fromProjectToModule\n , fromModuleToProject = Rule.initContextCreator fromModuleToProject |> Rule.withModuleKey\n , foldProjectContexts = foldProjectContexts\n }\n\nTODO Make this unavailable to module rules\n\n","type":"Review.Rule.ContextCreator Review.Rule.ModuleKey (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleNameLookupTable","comment":" Requests the module name lookup table for the types and functions inside a module.\n\nWhen encountering a `Expression.FunctionOrValue ModuleName String` (among other nodes where we refer to a function or value),\nthe module name available represents the module name that is in the source code. But that module name can be an alias to\na different import, or it can be empty, meaning that it refers to a local value or one that has been imported explicitly\nor implicitly. Resolving which module name the type or function can be a bit tricky sometimes, and I recommend against\ndoing it yourself.\n\n`elm-review` computes this for you already. Store this value inside your module context, then use\n[`ModuleNameLookupTable.moduleNameFor`](./Review-ModuleNameLookupTable#moduleNameFor) or\n[`ModuleNameLookupTable.moduleNameAt`](./Review-ModuleNameLookupTable#moduleNameAt) to get the name of the module the\ntype or value comes from.\n\n import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)\n\n type alias Context =\n { lookupTable : ModuleNameLookupTable }\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchemaUsingContextCreator \"NoHtmlButton\" contextCreator\n |> Rule.withExpressionEnterVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n |> Rule.ignoreErrorsForFiles [ \"src/Colors.elm\" ]\n\n contextCreator : Rule.ContextCreator () Context\n contextCreator =\n Rule.initContextCreator\n (\\lookupTable () -> { lookupTable = lookupTable })\n |> Rule.withModuleNameLookupTable\n\n expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context )\n expressionVisitor node context =\n case Node.value node of\n Expression.FunctionOrValue _ \"color\" ->\n if ModuleNameLookupTable.moduleNameFor context.lookupTable node == Just [ \"Css\" ] then\n ( [ Rule.error\n { message = \"Do not use `Css.color` directly, use the Colors module instead\"\n , details = [ \"We made a module which contains all the available colors of our design system. Use the functions in there instead.\" ]\n }\n (Node.range node)\n ]\n , context\n )\n\n else\n ( [], context )\n\n _ ->\n ( [], context )\n\nNote: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you should use this instead.\n\n","type":"Review.Rule.ContextCreator Review.ModuleNameLookupTable.ModuleNameLookupTable (from -> to) -> Review.Rule.ContextCreator from to"},{"name":"withModuleVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will\nvisit the project's Elm modules.\n\nA module visitor behaves like a module rule, except that it won't visit the\nproject files, as those have already been seen by other visitors for project rules (such\nas [`withElmJsonProjectVisitor`](#withElmJsonProjectVisitor)).\n\n`withModuleVisitor` takes a function that takes an already initialized module\nrule schema and adds visitors to it, using the same functions as for building a\n[`ModuleRuleSchema`](#ModuleRuleSchema).\n\nWhen you use `withModuleVisitor`, you will be required to use [`withModuleContext`](#withModuleContext),\nin order to specify how to create a `moduleContext` from a `projectContext` and vice-versa.\n\n","type":"(Review.Rule.ModuleRuleSchema {} moduleContext -> Review.Rule.ModuleRuleSchema { moduleSchemaState | hasAtLeastOneVisitor : () } moduleContext) -> Review.Rule.ProjectRuleSchema { projectSchemaState | canAddModuleVisitor : () } projectContext moduleContext -> Review.Rule.ProjectRuleSchema { projectSchemaState | canAddModuleVisitor : (), withModuleContext : Review.Rule.Required } projectContext moduleContext"},{"name":"withReadmeModuleVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit\nthe project's `README.md` file.\n","type":"(Maybe.Maybe String.String -> moduleContext -> moduleContext) -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | canCollectProjectData : () } moduleContext"},{"name":"withReadmeProjectVisitor","comment":" Add a visitor to the [`ProjectRuleSchema`](#ProjectRuleSchema) which will visit\nthe project's `README.md` file.\n\nIt works exactly like [`withReadmeModuleVisitor`](#withReadmeModuleVisitor).\nThe visitor will be called before any module is evaluated.\n\n","type":"(Maybe.Maybe { readmeKey : Review.Rule.ReadmeKey, content : String.String } -> projectContext -> ( List.List (Review.Rule.Error { useErrorForModule : () }), projectContext )) -> Review.Rule.ProjectRuleSchema schemaState projectContext moduleContext -> Review.Rule.ProjectRuleSchema { schemaState | hasAtLeastOneVisitor : () } projectContext moduleContext"},{"name":"withSimpleCommentsVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's comments.\n\nThis visitor will give you access to the list of all comments in the module all at once.\n\nThe following example forbids words like \"TODO\" appearing in a comment.\n\n import Elm.Syntax.Node as Node exposing (Node)\n import Elm.Syntax.Range exposing (Range)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoTodoComment\" ()\n |> Rule.withSimpleCommentsVisitor commentsVisitor\n |> Rule.fromModuleRuleSchema\n\n commentsVisitor : List (Node String) -> List (Error {})\n commentsVisitor comments =\n comments\n |> List.concatMap\n (\\commentNode ->\n String.indexes \"TODO\" (Node.value commentNode)\n |> List.map (errorAtPosition (Node.range commentNode))\n )\n\n errorAtPosition : Range -> Int -> Error {}\n errorAtPosition range index =\n Rule.error\n { message = \"TODO needs to be handled\"\n , details = [ \"At fruits.com, we prefer not to have lingering TODO comments. Either fix the TODO now or create an issue for it.\" ]\n }\n -- Here you would ideally only target the TODO keyword\n -- or the rest of the line it appears on,\n -- so you would change `range` using `index`.\n range\n\nNote: `withSimpleCommentsVisitor` is a simplified version of [`withCommentsVisitor`](#withCommentsVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withCommentsVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(List.List (Elm.Syntax.Node.Node String.String) -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleDeclarationVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[declaration statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Declaration)\n(`someVar = add 1 2`, `type Bool = True | False`, `port output : Json.Encode.Value -> Cmd msg`)\nand report patterns. The declarations will be visited in the order of their definition.\n\nThe following example forbids declaring a function or a value without a type\nannotation.\n\n import Elm.Syntax.Declaration as Declaration exposing (Declaration)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoMissingTypeAnnotation\" ()\n |> Rule.withSimpleDeclarationVisitor declarationVisitor\n |> Rule.fromModuleRuleSchema\n\n declarationVisitor : Node Declaration -> List (Error {})\n declarationVisitor node =\n case Node.value node of\n Declaration.FunctionDeclaration { signature, declaration } ->\n case signature of\n Just _ ->\n []\n\n Nothing ->\n let\n functionName : String\n functionName =\n declaration |> Node.value |> .name |> Node.value\n in\n [ Rule.error\n { message = \"Missing type annotation for `\" ++ functionName ++ \"`\"\n , details =\n [ \"Type annotations are very helpful for people who read your code. It can give a lot of information without having to read the contents of the function. When encountering problems, the compiler will also give much more precise and helpful information to help you solve the problem.\"\n , \"To add a type annotation, add a line like `\" functionName ++ \" : ()`, and replace the `()` by the type of the function. If you don't replace `()`, the compiler should give you a suggestion of what the type should be.\"\n ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\nNote: `withSimpleDeclarationVisitor` is a simplified version of [`withDeclarationEnterVisitor`](#withDeclarationEnterVisitor),\nwhich isn't passed a `context` and doesn't return one either. You can use `withSimpleDeclarationVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Declaration.Declaration -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleExpressionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's\n[expressions](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Expression)\n(`1`, `True`, `add 1 2`, `1 + 2`). The expressions are visited in pre-order\ndepth-first search, meaning that an expression will be visited, then its first\nchild, the first child's children (and so on), then the second child (and so on).\n\nThe following example forbids using the Debug module.\n\n import Elm.Syntax.Expression as Expression exposing (Expression)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoDebug\" ()\n |> Rule.withSimpleExpressionVisitor expressionVisitor\n |> Rule.fromModuleRuleSchema\n\n expressionVisitor : Node Expression -> List (Error {})\n expressionVisitor node =\n case Node.value node of\n Expression.FunctionOrValue moduleName fnName ->\n if List.member \"Debug\" moduleName then\n [ Rule.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"The `Debug` module is useful when developing, but is not meant to be shipped to production or published in a package. I suggest removing its use before committing and attempting to push to production.\" ]\n }\n (Node.range node)\n ]\n\n else\n []\n\n _ ->\n []\n\nNote: `withSimpleExpressionVisitor` is a simplified version of [`withExpressionEnterVisitor`](#withExpressionEnterVisitor),\nwhich isn't passed a `context` and doesn't return one either. You can use `withSimpleExpressionVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Expression.Expression -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleImportVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's [import statements](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Import) (`import Html as H exposing (div)`) in order of their definition and report patterns.\n\nThe following example forbids using the core Html package and suggests using\n`elm-css` instead.\n\n import Elm.Syntax.Import exposing (Import)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoCoreHtml\" ()\n |> Rule.withSimpleImportVisitor importVisitor\n |> Rule.fromModuleRuleSchema\n\n importVisitor : Node Import -> List (Error {})\n importVisitor node =\n let\n moduleName : List String\n moduleName =\n node\n |> Node.value\n |> .moduleName\n |> Node.value\n in\n case moduleName of\n [ \"Html\" ] ->\n [ Rule.error\n { message = \"Use `elm-css` instead of the core HTML package.\"\n , details =\n [ \"At fruits.com, we chose to use the `elm-css` package (https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/Css) to build our HTML and CSS rather than the core Html package. To keep things simple, we think it is best to not mix these different libraries.\"\n , \"The API is very similar, but instead of using the `Html` module, use the `Html.Styled`. CSS is then defined using the Html.Styled.Attributes.css function (https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/Html-Styled-Attributes#css).\"\n ]\n }\n (Node.range node)\n ]\n\n _ ->\n []\n\nNote: `withSimpleImportVisitor` is a simplified version of [`withImportVisitor`](#withImportVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withSimpleImportVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Import.Import -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"},{"name":"withSimpleModuleDefinitionVisitor","comment":" Add a visitor to the [`ModuleRuleSchema`](#ModuleRuleSchema) which will visit the module's [module definition](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Module) (`module SomeModuleName exposing (a, b)`) and report patterns.\n\nThe following example forbids having `_` in any part of a module name.\n\n import Elm.Syntax.Module as Module exposing (Module)\n import Elm.Syntax.Node as Node exposing (Node)\n import Review.Rule as Rule exposing (Error, Rule)\n\n rule : Rule\n rule =\n Rule.newModuleRuleSchema \"NoUnderscoreInModuleName\" ()\n |> Rule.withSimpleModuleDefinitionVisitor moduleDefinitionVisitor\n |> Rule.fromModuleRuleSchema\n\n moduleDefinitionVisitor : Node Module -> List (Error {})\n moduleDefinitionVisitor node =\n if List.any (String.contains \"\") (Node.value node |> Module.moduleName) then\n [ Rule.error\n { message = \"Do not use `_` in a module name\"\n , details = [ \"By convention, Elm modules names use Pascal case (like `MyModuleName`). Please rename your module using this format.\" ]\n }\n (Node.range node)\n ]\n\n else\n []\n\nNote: `withSimpleModuleDefinitionVisitor` is a simplified version of [`withModuleDefinitionVisitor`](#withModuleDefinitionVisitor),\nwhich isn't passed a `context` and doesn't return one. You can use `withSimpleModuleDefinitionVisitor` even if you use \"non-simple with\\*\" functions.\n\n","type":"(Elm.Syntax.Node.Node Elm.Syntax.Module.Module -> List.List (Review.Rule.Error {})) -> Review.Rule.ModuleRuleSchema schemaState moduleContext -> Review.Rule.ModuleRuleSchema { schemaState | hasAtLeastOneVisitor : () } moduleContext"}],"binops":[]},{"name":"Review.Test","comment":" Module that helps you test your rules, using [`elm-test`](https://package.elm-lang.org/packages/elm-explorations/test/latest/).\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should not report anything when \" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = foo n\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n , test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\" \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\n\n# Strategies for effective testing\n\n\n## Use Test-Driven Development\n\nWriting a rule is a process that works really well with the Test-Driven\nDevelopment process loop, which is:\n\n - Before writing any code, write a failing test.\n - Run the test and make sure that it is failing, otherwise you can't be\n sure that the test is well-written.\n - Write the simplest (almost stupid) code to make the test pass\n - Run the tests again and make sure that the test is passing, and that you\n didn't break any previous tests\n - Optionally, refactor your code but be sure not to change the behavior of the\n implementation. You should not add support for new patterns, as you will\n want to write tests for those first.\n\nThen repeat for every pattern yoy wish to handle.\n\n\n## Have a good title\n\nA good test title explains\n\n - what is tested - Probably the rule, but making it explicit\n in a [`describe`](https://package.elm-lang.org/packages/elm-explorations/test/latest/Test#describe)\n might improve your test report. Or maybe you are testing a sub-part of the rule,\n and you can name it explictly.\n - what should happen: (not) reporting an error, fix by , ...\n - when: what is the situation that this test sets up?\n\nIdeally, by only reading through the test titles, someone else should be able to\nrewrite the rule you are testing.\n\n\n## What should you test?\n\nYou should test the scenarios where you expect the rule to report something. At\nthe same time, you should also test when it shouldn't. I encourage writing tests\nto make sure that things that are similar to what you want to report are not\nreported.\n\nFor instance, if you wish to report uses of variables named `foo`, write a test\nthat ensures that the use of variables named differently does not get reported.\n\nTests are pretty cheap, and in the case of rules, it is probably better to have\ntoo many tests rather than too few, since the behavior of a rule rarely changes\ndrastically.\n\n\n# Design goals\n\nIf you are interested, you can read\n[the design goals](https://github.com/jfmengels/elm-review/blob/master/documentation/design/test-module.md)\nfor this module.\n\n\n# Running tests\n\n@docs ReviewResult, run, runWithProjectData, runOnModules, runOnModulesWithProjectData\n\n\n# Making assertions\n\n@docs ExpectedError, expectNoErrors, expectErrors, error, atExactly, whenFixed, expectErrorsForModules, expectErrorsForElmJson, expectErrorsForReadme\n\n","unions":[{"name":"ExpectedError","comment":" An expectation for an error. Use [`error`](#error) to create one.\n","args":[],"cases":[]},{"name":"ReviewResult","comment":" The result of running a rule on a `String` containing source code.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"atExactly","comment":" Precise the exact position where the error should be shown to the user. This\nis only necessary when the `under` field is ambiguous.\n\n`atExactly` takes a record with start and end positions.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report multiple Debug.log calls\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"foo\" z\n b = Debug.log \"foo\" z\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.atExactly { start = { row = 4, column = 5 }, end = { row = 4, column = 14 } }\n , Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.atExactly { start = { row = 5, column = 5 }, end = { row = 5, column = 14 } }\n ]\n ]\n\nTip: By default, do not use this function. If the test fails because there is some\nambiguity, the test error will give you a recommendation of what to use as a parameter\nof `atExactly`, so you do not have to bother writing this hard-to-write argument yourself.\n\n","type":"{ start : { row : Basics.Int, column : Basics.Int }, end : { row : Basics.Int, column : Basics.Int } } -> Review.Test.ExpectedError -> Review.Test.ExpectedError"},{"name":"error","comment":" Create an expectation for an error.\n\n`message` should be the message you're expecting to be shown to the user.\n\n`under` is the part of the code where you are expecting the error to be shown to\nthe user. If it helps, imagine `under` to be the text under which the squiggly\nlines will appear if the error appeared in an editor.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\\\"\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\nIf there are multiple locations where the value of `under` appears, the test will\nfail unless you use [`atExactly`](#atExactly) to remove any ambiguity of where the\nerror should be used.\n\n","type":"{ message : String.String, details : List.List String.String, under : String.String } -> Review.Test.ExpectedError"},{"name":"expectErrors","comment":" Assert that the rule reported some errors, by specifying which ones.\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\nThe errors should be in the order of where they appear in the source code. An error\nat the start of the source code should appear earlier in the list than\nan error at the end of the source code.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report Debug.log use\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = Debug.log \"some\" \"message\"\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n ]\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForElmJson","comment":" Assert that the rule reported some errors for the `elm.json` file, by specifying which ones.\n\n test \"report an error when a module is unused\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectErrorsForElmJson\n [ Review.Test.error\n { message = \"Unused dependency `author/package`\"\n , details = [ \"Dependency should be removed\" ]\n , under = \"author/package\"\n }\n ]\n\nAlternatively, or if you need to specify errors for other files too, you can use [`expectErrorsForModules`](#expectErrorsForModules), specifying `elm.json` as the module name.\n\n sourceCode\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\", [ Review.Test.error someErrorModuleB ] )\n , ( \"elm.json\", [ Review.Test.error someErrorForElmJson ] )\n ]\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForModules","comment":" Assert that the rule reported some errors, by specifying which ones and the\nmodule for which they were reported.\n\nThis is the same as [`expectErrors`](#expectErrors), but for when you used\n[`runOnModules`](#runOnModules) or [`runOnModulesWithProjectData`](#runOnModulesWithProjectData).\nto create the test. When using those, the errors you expect need to be associated\nwith a module. If we don't specify this, your tests might pass because you\nexpected the right errors, but they may be reported for the wrong module!\n\nThe expected errors are tupled: the first element is the module name\n(for example: `List` or `My.Module.Name`) and the second element is the list of\nerrors you expect to be reported.\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\nThe errors should be in the order of where they appear in the source code. An error\nat the start of the source code should appear earlier in the list than\nan error at the end of the source code.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n all : Test\n all =\n test \"should report an error when a module uses `Debug.log`\" <|\n \\() ->\n [ \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\", \"\"\"\n module ModuleB exposing (a)\n a = Debug.log \"log\" 1\"\"\" ]\n |> Review.Test.runOnModules rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\"\n , [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n ]\n )\n ]\n\n","type":"List.List ( String.String, List.List Review.Test.ExpectedError ) -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectErrorsForReadme","comment":" Assert that the rule reported some errors for the `README.md` file, by specifying which ones.\n\n test \"report an error when a module is unused\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addReadme { path = \"README.md\", context = \"# Project\\n...\" }\n in\n \"\"\"\n module ModuleA exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectErrorsForReadme\n [ Review.Test.error\n { message = \"Invalid link\"\n , details = [ \"README contains an invalid link\" ]\n , under = \"htt://example.com\"\n }\n ]\n\nAlternatively, or if you need to specify errors for other files too, you can use [`expectErrorsForModules`](#expectErrorsForModules), specifying `README.md` as the module name.\n\n sourceCode\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectErrorsForModules\n [ ( \"ModuleB\", [ Review.Test.error someErrorModuleB ] )\n , ( \"README.md\", [ Review.Test.error someErrorForReadme ] )\n ]\n\nAssert which errors are reported using [`error`](#error). The test will fail if\na different number of errors than expected are reported, or if the message or the\nlocation is incorrect.\n\n","type":"List.List Review.Test.ExpectedError -> Review.Test.ReviewResult -> Expect.Expectation"},{"name":"expectNoErrors","comment":" Assert that the rule reported no errors. Note, this is equivalent to using [`expectErrors`](#expectErrors)\nlike `expectErrors []`.\n\n import Review.Test\n import Test exposing (Test, describe, test)\n import The.Rule.You.Want.To.Test exposing (rule)\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should not report anything when \" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = foo n\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n ]\n\n","type":"Review.Test.ReviewResult -> Expect.Expectation"},{"name":"run","comment":" Run a `Rule` on a `String` containing source code. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrors`](#expectErrors) to assert\nthe errors reported by the rule.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n \"\"\"\n module SomeModule exposing (a)\n a = 1\"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectNoErrors\n\nThe source code needs to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a simpler version of [`runWithProjectData`](#runWithProjectData).\nIf your rule is interested in project related details, then you should use\n[`runWithProjectData`](#runWithProjectData) instead.\n\n","type":"Review.Rule.Rule -> String.String -> Review.Test.ReviewResult"},{"name":"runOnModules","comment":" Run a `Rule` on several modules. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrorsForModules`](#expectErrorsForModules) to assert\nthe errors reported by the rule.\n\nThis is the same as [`run`](#run), but you can pass several modules.\nThis is especially useful to test rules created with\n[`Review.Rule.newProjectRuleSchema`](./Review-Rule#newProjectRuleSchema), that look at\nseveral files, and where the context of the project is important.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n [ \"\"\"\n module A exposing (a)\n a = 1\"\"\", \"\"\"\n module B exposing (a)\n a = 1\"\"\" ]\n |> Review.Test.runOnModules rule\n |> Review.Test.expectNoErrors\n\nThe source codes need to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of each file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a simpler version of [`runOnModulesWithProjectData`](#runOnModulesWithProjectData).\nIf your rule is interested in project related details, then you should use\n[`runOnModulesWithProjectData`](#runOnModulesWithProjectData) instead.\n\n","type":"Review.Rule.Rule -> List.List String.String -> Review.Test.ReviewResult"},{"name":"runOnModulesWithProjectData","comment":" Run a `Rule` on several modules. You can then use\n[`expectNoErrors`](#expectNoErrors) or [`expectErrorsForModules`](#expectErrorsForModules) to assert\nthe errors reported by the rule.\n\nThis is basically the same as [`run`](#run), but you can pass several modules.\nThis is especially useful to test rules created with\n[`Review.Rule.newProjectRuleSchema`](./Review-Rule#newProjectRuleSchema), that look at\nseveral modules, and where the context of the project is important.\n\n import My.Rule exposing (rule)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n [ \"\"\"\n module A exposing (a)\n a = 1\"\"\", \"\"\"\n module B exposing (a)\n a = 1\"\"\" ]\n |> Review.Test.runOnModulesWithProjectData project rule\n |> Review.Test.expectNoErrors\n\nThe source codes need to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of each file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a more complex version of [`runOnModules`](#runOnModules). If your rule is not\ninterested in project related details, then you should use [`runOnModules`](#runOnModules) instead.\n\n","type":"Review.Project.Project -> Review.Rule.Rule -> List.List String.String -> Review.Test.ReviewResult"},{"name":"runWithProjectData","comment":" Run a `Rule` on a `String` containing source code, with data about the\nproject loaded, such as the contents of `elm.json` file.\n\n import My.Rule exposing (rule)\n import Review.Project as Project exposing (Project)\n import Review.Test\n import Test exposing (Test, test)\n\n all : Test\n all =\n test \"test title\" <|\n \\() ->\n let\n project : Project\n project =\n Project.new\n |> Project.addElmJson elmJsonToConstructManually\n in\n \"\"\"module SomeModule exposing (a)\n a = 1\"\"\"\n |> Review.Test.runWithProjectData project rule\n |> Review.Test.expectNoErrors\n\nThe source code needs to be syntactically valid Elm code. If the code\ncan't be parsed, the test will fail regardless of the expectations you set on it.\n\nNote that to be syntactically valid, you need at least a module declaration at the\ntop of the file (like `module A exposing (..)`) and one declaration (like `a = 1`).\nYou can't just have an expression like `1 + 2`.\n\nNote: This is a more complex version of [`run`](#run). If your rule is not\ninterested in project related details, then you should use [`run`](#run) instead.\n\n","type":"Review.Project.Project -> Review.Rule.Rule -> String.String -> Review.Test.ReviewResult"},{"name":"whenFixed","comment":" Create an expectation that the error provides an automatic fix, meaning that it used\nfunctions like [`errorWithFix`](./Review-Rule#errorWithFix), and an expectation of what the source\ncode should be after the error's fix have been applied.\n\nIn the absence of `whenFixed`, the test will fail if the error provides a fix.\nIn other words, you only need to use this function if the error provides a fix.\n\n tests : Test\n tests =\n describe \"The.Rule.You.Want.To.Test\"\n [ test \"should report multiple Debug.log calls\" <|\n \\() ->\n \"\"\"module A exposing (..)\n a = 1\n b = Debug.log \"foo\" 2\n \"\"\"\n |> Review.Test.run rule\n |> Review.Test.expectErrors\n [ Review.Test.error\n { message = \"Remove the use of `Debug` before shipping to production\"\n , details = [ \"Details about the error\" ]\n , under = \"Debug.log\"\n }\n |> Review.Test.whenFixed \"\"\"module SomeModule exposing (b)\n a = 1\n b = 2\n \"\"\"\n ]\n ]\n\n","type":"String.String -> Review.Test.ExpectedError -> Review.Test.ExpectedError"}],"binops":[]}] \ No newline at end of file diff --git a/elm.json b/elm.json index 02a3dd68..5c0884c8 100644 --- a/elm.json +++ b/elm.json @@ -6,6 +6,7 @@ "version": "2.2.0", "exposed-modules": [ "Review.Rule", + "Review.ModuleNameLookupTable", "Review.Project", "Review.Project.Dependency", "Review.Fix", diff --git a/src/Review/ModuleNameLookupTable.elm b/src/Review/ModuleNameLookupTable.elm index 6ea98c37..6c44a245 100644 --- a/src/Review/ModuleNameLookupTable.elm +++ b/src/Review/ModuleNameLookupTable.elm @@ -1,8 +1,23 @@ -module Review.ModuleNameLookupTable exposing - ( ModuleNameLookupTable - , moduleNameAt - , moduleNameFor - ) +module Review.ModuleNameLookupTable exposing (ModuleNameLookupTable, moduleNameFor, moduleNameAt) + +{-| Looks up the name of the module a function or type comes from based on the position of the element in the module's AST. + +When encountering a `Expression.FunctionOrValue ModuleName String` (among other nodes where we refer to a function or value), +the module name available represents the module name that is in the source code. But that module name can be an alias to +a different import, or it can be empty, meaning that it refers to a local value or one that has been imported explicitly +or implicitly. Resolving which module name the type or function can be a bit tricky sometimes, and I recommend against +doing it yourself. + +`elm-review` computes this for you already. Store this value inside your module context, then use +[`ModuleNameLookupTable.moduleNameFor`](./Review-ModuleNameLookupTable#moduleNameFor) or +[`ModuleNameLookupTable.moduleNameAt`](./Review-ModuleNameLookupTable#moduleNameAt) to get the name of the module the +type or value comes from. + +@docs ModuleNameLookupTable, moduleNameFor, moduleNameAt + +Note: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you should use this instead. + +-} import Dict import Elm.Syntax.ModuleName exposing (ModuleName) @@ -11,15 +26,80 @@ import Elm.Syntax.Range exposing (Range) import Review.ModuleNameLookupTable.Internal as Internal +{-| Associates positions in the AST of a module to the name of the module that the contained variable or type originates +from. +-} type alias ModuleNameLookupTable = Internal.ModuleNameLookupTable +{-| Returns the name of the module the type or value referred to by this [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node). + +The function returns `Just []` if the type or value was defined in this module. It returns `Just moduleName` if the Node is among these kinds of AST nodes (and `Nothing` for all the others): + + - Expression.FunctionOrValue + - `nodeForTheName` in `Expression.RecordUpdateExpression nodeForTheName modifiers` + - `nodeForTheName` in `TypeAnnotation.Typed nodeForTheName args` + - `nodeForTheName` in `Pattern.NamedPattern nodeForTheName subPatterns` + +```elm +expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context ) +expressionVisitor node context = + case Node.value node of + Expression.FunctionOrValue _ "color" -> + if ModuleNameLookupTable.moduleNameFor context.lookupTable node == Just [ "Css" ] then + ( [ Rule.error + { message = "Do not use `Css.color` directly, use the Colors module instead" + , details = [ "We made a module which contains all the available colors of our design system. Use the functions in there instead." ] + } + (Node.range node) + ] + , context + ) + + else + ( [], context ) + + _ -> + ( [], context ) +``` + +Note: If using a `Range` is easier in your situation than using a `Node`, use [`moduleNameAt`](#moduleNameAt) instead. + +-} moduleNameFor : ModuleNameLookupTable -> Node a -> Maybe ModuleName moduleNameFor (Internal.ModuleNameLookupTable dict) (Node range _) = Dict.get (Internal.toRangeLike range) dict +{-| Returns the name of the module the type or value referred to by this [`Node`](https://package.elm-lang.org/packages/stil4m/elm-syntax/7.1.0/Elm-Syntax-Node#Node). + +The function returns `Just []` if the type or value was defined in this module. It returns `Just moduleName` if the Node is among these kinds of AST nodes (and `Nothing` for all the others): + + - Expression.FunctionOrValue + - `nodeForTheName` in `Expression.RecordUpdateExpression nodeForTheName modifiers` + - `nodeForTheName` in `TypeAnnotation.Typed nodeForTheName args` + - `nodeForTheName` in `Pattern.NamedPattern nodeForTheName subPatterns` + +```elm +expressionVisitor : Node Expression -> Context -> ( List (Error {}), Context ) +expressionVisitor node context = + case Node.value node of + Expression.RecordUpdateExpr (Node range name) _ -> + case ModuleNameLookupTable.moduleNameAt context.lookupTable range of + Just moduleName -> + ( [], markVariableAsUsed ( moduleName, name ) context ) + + Nothing -> + ( [], context ) + + _ -> + ( [], context ) +``` + +Note: If using a `Node` is easier in your situation than using a `Range`, use [`moduleNameFor`](#moduleNameFor) instead. + +-} moduleNameAt : ModuleNameLookupTable -> Range -> Maybe ModuleName moduleNameAt (Internal.ModuleNameLookupTable dict) range = Dict.get (Internal.toRangeLike range) dict diff --git a/src/Review/Rule.elm b/src/Review/Rule.elm index 54893d4f..63fb9a0c 100644 --- a/src/Review/Rule.elm +++ b/src/Review/Rule.elm @@ -4100,10 +4100,11 @@ withMetadata (ContextCreator fn (RequestedData requested)) = {-| Requests the module name lookup table for the types and functions inside a module. -When encountering a `Expression.FunctionOrValue ModuleName String`, the module name available represents the module name -that is in the source code. But that module name can be an alias to a different import, or it can be empty, meaning that -it refers to a local value or one that has been imported explicitly or implicitly. Resolving which module name the type -or function can be a bit tricky sometimes, and I recommend against doing it yourself. +When encountering a `Expression.FunctionOrValue ModuleName String` (among other nodes where we refer to a function or value), +the module name available represents the module name that is in the source code. But that module name can be an alias to +a different import, or it can be empty, meaning that it refers to a local value or one that has been imported explicitly +or implicitly. Resolving which module name the type or function can be a bit tricky sometimes, and I recommend against +doing it yourself. `elm-review` computes this for you already. Store this value inside your module context, then use [`ModuleNameLookupTable.moduleNameFor`](./Review-ModuleNameLookupTable#moduleNameFor) or @@ -4148,7 +4149,7 @@ type or value comes from. _ -> ( [], context ) -Note: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you might want to use this instead. +Note: If you have been using [`elm-review-scope`](https://github.com/jfmengels/elm-review-scope) before, you should use this instead. -} withModuleNameLookupTable : ContextCreator ModuleNameLookupTable (from -> to) -> ContextCreator from to