├── .formatter.exs ├── .gitignore ├── LICENSE.md ├── README.md ├── config └── config.exs ├── examples ├── foo.ex └── todo.ex ├── lib ├── fix_warnings.ex ├── log_parser.ex ├── log_reader.ex ├── mix │ └── tasks │ │ └── fix_warnings.ex ├── patch │ ├── unused_alias.ex │ └── unused_variable.ex └── util.ex ├── mix.exs ├── mix.lock ├── overview.md └── test ├── fix_warnings_test.exs ├── test.log ├── test_deprecated.log ├── test_helper.exs ├── todo.log └── util_test.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 3 | ] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 Chris McCord 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FixWarnings 2 | 3 | Automatically fixes compiler warnings in your Elixir project. 4 | 5 | Does that look familiar? 6 | 7 | ``` 8 | Compiling 5 files (.ex) 9 | warning: variable "params" is unused 10 | lib/controller.ex:24 11 | 12 | ... 500 lines more... 13 | 14 | warning: variable "curr_line" is unused 15 | lib/foo.ex:31 16 | ``` 17 | 18 | FixWarnings automatically fixes the trivial warnings directly in your Elixir source code. It removes unused aliases and adds a `_` prefix to unused variables with. 19 | 20 | ## Alpha Warning 21 | 22 | I extracted this from a quickly hacked together script, which worked well for me. But don't trust this blindly yet, verify with git diff first. 23 | 24 | ## Guide 25 | 26 | Add `fix_warnings` to your mix.exs. 27 | 28 | ```elixir 29 | def deps do 30 | [ 31 | {:fix_warnings, "~> 0.1.0", only: :dev} 32 | ] 33 | end 34 | ``` 35 | 36 | Install dependency 37 | 38 | ``` 39 | mix deps.get 40 | ``` 41 | 42 | Run fix_warnings: 43 | ``` 44 | mix fix_warnings 45 | ``` 46 | 47 | Or run with explicitly captured logs: 48 | ``` 49 | mix compile --force &> path/to/output.log 50 | 51 | mix fix_warnings -f path/to/output.log 52 | # or mix fix_warnings --file=path/to/output.log 53 | ``` 54 | 55 | Enjoy 56 | 57 | ``` 58 | git diff 59 | ``` 60 | 61 | ## TODOs (PRs welcome) 62 | 63 | - There might be a few edge-cases 64 | - Add more warnings 65 | - Refactor mix task 66 | - Does not seem to capture warnings in `.exs` files 67 | - Add support for `preview` flag 68 | - this should be the default, so maybe add support for `force` flag? 69 | - Add diffing output -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure for your application as: 12 | # 13 | # config :fix_warnings, key: :value 14 | # 15 | # And access this configuration in your application as: 16 | # 17 | # Application.get_env(:fix_warnings, :key) 18 | # 19 | # Or configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /examples/foo.ex: -------------------------------------------------------------------------------- 1 | 2 | defmodule Examples.Bar do 3 | end 4 | 5 | defmodule Examples.Foo do 6 | alias Examples.Bar 7 | alias String 8 | 9 | def foo(unused_param) do 10 | String.length("hello") 11 | end 12 | 13 | def foo(unused_param1, unused_param2) do 14 | end 15 | end -------------------------------------------------------------------------------- /examples/todo.ex: -------------------------------------------------------------------------------- 1 | 2 | defmodule Examples.Bar do 3 | end 4 | defmodule Examples.Baz do 5 | end 6 | 7 | defmodule Examples.AliasWithCurlyBrackets do 8 | alias Examples.{Bar,Baz} 9 | 10 | Baz 11 | 12 | def parse_headers(headers, headers1) do 13 | headers1 14 | end 15 | 16 | def parse_params(%{"title" => title}), do: nil 17 | def parse_params(%{title: title}), do: nil 18 | end -------------------------------------------------------------------------------- /lib/fix_warnings.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings do 2 | @moduledoc """ 3 | FixWarnings automatically fixes the most common Elixir warnings in your source code. 4 | 5 | Currently supported fixes: 6 | 7 | ## warning: variable "foo" is unused 8 | 9 | Prefix variable `foo` with a `_`: `_foo` 10 | 11 | ## warning: unused alias Baz 12 | 13 | Automatically deletes the line with an unused alias. 14 | 15 | Edge Cases: 16 | - TODO: alias Foo.{Bar, Baz} 17 | 18 | """ 19 | 20 | alias FixWarnings.LogParser 21 | alias FixWarnings.LogReader 22 | alias FixWarnings.Patch.UnusedAlias 23 | alias FixWarnings.Patch.UnusedVariable 24 | 25 | def enabled_patches do 26 | [ 27 | UnusedAlias, 28 | UnusedVariable 29 | ] 30 | end 31 | 32 | def run(args, _mode \\ :preview) do 33 | # IO.puts("parsing log: #{path}") 34 | 35 | args 36 | |> changes() 37 | |> Enum.filter(fn {path, _content} -> File.exists?(path) end) 38 | |> Enum.each(fn {path, content} -> 39 | # IO.puts("- writing #{path}") 40 | File.write(path, content) 41 | end) 42 | end 43 | 44 | def changes(args) do 45 | args 46 | |> patches_per_file() 47 | |> Enum.map(fn {path, patches} -> {path, patch(path, patches)} end) 48 | |> Map.new() 49 | end 50 | 51 | defp patches_per_file(args) do 52 | args 53 | |> read!() 54 | |> LogParser.parse() 55 | |> Enum.group_by(fn x -> x.path end) 56 | end 57 | 58 | defp read!(%{file: path}), do: LogReader.read_from_file!(path) 59 | defp read!(_args), do: LogReader.read_from_output!() 60 | 61 | def patch(path, patches) do 62 | # IO.puts("patching source: #{path}") 63 | 64 | # group by line number. There can be multiple patches per line. 65 | # e.g. def foo(unused_param1, unused_param2) do 66 | patches = Enum.group_by(patches, fn p -> p.line - 1 end) 67 | 68 | case File.read(path) do 69 | {:ok, data} -> 70 | data 71 | |> String.split("\n") 72 | |> Enum.with_index() 73 | |> Enum.map(fn {line, no} -> patch_line(line, patches[no]) end) 74 | |> Enum.reject(&is_nil/1) 75 | |> Enum.join("\n") 76 | 77 | _ -> 78 | nil 79 | end 80 | end 81 | 82 | def patch_line(line, nil), do: line 83 | 84 | def patch_line(line, patches) do 85 | Enum.reduce(patches, line, fn patch, line -> 86 | # IO.puts("patching line: #{line}") 87 | # edge-case: line can be nil, e.g. when it was removed previously 88 | if line, do: patch.__struct__.patch(line, patch) 89 | end) 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/log_parser.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.LogParser do 2 | @moduledoc """ 3 | Parses a file containing log output into a list of patches. 4 | 5 | ## Exammple 6 | 7 | log_data = "warning: unused alias Bar\n examples/foo.ex:13" 8 | FixWarnings.LogParser.parse(log_data) 9 | > [%FixWarnings.Patch.UnusedAlias{path: "examples/foo.ex", line: 13, element: "Bar"}] 10 | 11 | """ 12 | 13 | def parse(str) do 14 | lines = String.split(str, "\n") 15 | parse_lines([], lines) 16 | end 17 | 18 | defp parse_lines(fixes, []), do: fixes 19 | 20 | defp parse_lines(fixes, [curr_line | tail]) do 21 | definition = 22 | Enum.find(FixWarnings.enabled_patches(), fn d -> 23 | d.match?(curr_line) 24 | end) 25 | 26 | _fixes = 27 | if definition do 28 | case definition.build(curr_line, tail) do 29 | {:ok, fix, tail} -> 30 | parse_lines([fix | fixes], tail) 31 | 32 | _ -> 33 | # log: warning 34 | parse_lines(fixes, tail) 35 | end 36 | else 37 | parse_lines(fixes, tail) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/log_reader.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.LogReader do 2 | def read_from_file!(nil) do 3 | raise "Error: Please provide path. Example\n. mix fix_warnings -f path/to/output.log" 4 | end 5 | 6 | def read_from_file!(path) do 7 | if File.exists?(path) do 8 | File.read!(path) 9 | else 10 | raise "Error: file #{path} does not exists. " 11 | end 12 | end 13 | 14 | def read_from_output! do 15 | System.cmd("mix", ~w/clean/) 16 | 17 | {output, _exit_status} = System.cmd("mix", ~w/compile --force/, stderr_to_stdout: true) 18 | 19 | output 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/mix/tasks/fix_warnings.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.FixWarnings do 2 | use Mix.Task 3 | 4 | @shortdoc "Patches warning (and overwrites) your files" 5 | 6 | @moduledoc """ 7 | Automatically fixes compiler warnings in your Elixir project. 8 | 9 | $ mix clean 10 | # clear 11 | # mix 12 | # # Copy output to path/to/output.log 13 | $ mix fix_warnings -f path/to/output.log 14 | 15 | - The `-f` option indicates the path where you have copied the log output 16 | - The `-q` or --quiet flag to surpress the [Yn] confirm prompt 17 | 18 | See [https://github.com/hasclass/fix_warnings](hasclass/fix_warnings) for details on how to run. 19 | """ 20 | 21 | @doc false 22 | def run(args) do 23 | {args, _, _} = 24 | OptionParser.parse(args, 25 | aliases: [f: :file, q: :quiet], 26 | strict: [file: :string, quiet: :boolean] 27 | ) 28 | 29 | args = Map.new(args) 30 | 31 | answer = 32 | if args[:quiet] do 33 | "y" 34 | else 35 | IO.puts("\n\n") 36 | IO.puts("Warning: This will **overwrite** your source code.\n") 37 | IO.puts("Are you sure? [Yn]:") 38 | 39 | IO.read(:stdio, :line) 40 | |> String.trim() 41 | |> String.downcase() 42 | end 43 | 44 | if answer == "y" do 45 | FixWarnings.run(args) 46 | else 47 | IO.puts("Cancelled") 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/patch/unused_alias.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.Patch.UnusedAlias do 2 | @moduledoc """ 3 | Automatically removes unused aliases from a module. 4 | 5 | ## Example 6 | 7 | warning: unused alias Bar 8 | 9 | # Before: 10 | defmodule Foo 11 | alias Bar 12 | end 13 | 14 | # After the patch 15 | defmodule Foo 16 | end 17 | 18 | """ 19 | 20 | defstruct [:path, :line, :element] 21 | 22 | alias FixWarnings.Util 23 | alias FixWarnings.Patch.UnusedAlias 24 | 25 | @matcher ~r/warning: unused alias (.+)/ 26 | 27 | def match?(line) do 28 | !is_nil(alias_name(line)) 29 | end 30 | 31 | def build(_curr_line, []), do: {:error} 32 | 33 | def build(curr_line, tail) do 34 | [file_loc | tail] = tail 35 | 36 | case Util.parse_source(file_loc) do 37 | {path, no} -> 38 | fix = %UnusedAlias{path: path, line: no, element: alias_name(curr_line)} 39 | {:ok, fix, tail} 40 | 41 | _ -> 42 | {:error} 43 | end 44 | end 45 | 46 | def patch(line, patch) do 47 | if String.contains?(line, "{") do 48 | el = patch.element 49 | 50 | ~r/(\W)(#{el}\W)/ 51 | |> Regex.replace(line, "\\g{1}") 52 | else 53 | # remove this line 54 | nil 55 | end 56 | end 57 | 58 | defp alias_name(line) do 59 | case Regex.scan(@matcher, line) do 60 | [[_, name]] -> name 61 | _ -> nil 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/patch/unused_variable.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.Patch.UnusedVariable do 2 | @moduledoc """ 3 | Automatically prefixes unused variables with a "_". This is the minimal 4 | invasive patch, as it preserves meaning from variable names. 5 | 6 | ## Example 7 | 8 | warning: variable "my_param" is unused 9 | 10 | # Before 11 | def foo(my_param) do 12 | nil 13 | end 14 | 15 | # After 16 | def foo(_my_param) do 17 | nil 18 | end 19 | """ 20 | 21 | defstruct [:path, :line, :element] 22 | 23 | alias FixWarnings.Util 24 | alias FixWarnings.Patch.UnusedVariable 25 | 26 | @matcher ~r/warning: variable "(.+)" is unused/ 27 | 28 | def match?(line) do 29 | !is_nil(element_name(line)) 30 | end 31 | 32 | def build(_curr_line, []), do: {:error} 33 | 34 | def build(curr_line, tail) do 35 | [file_loc | tail] = tail 36 | 37 | case Util.parse_source(file_loc) do 38 | {path, no} -> 39 | fix = %UnusedVariable{path: path, line: no, element: element_name(curr_line)} 40 | {:ok, fix, tail} 41 | 42 | _ -> 43 | {:error} 44 | end 45 | end 46 | 47 | def patch(line, patch) do 48 | Util.prefix_with_underscore(line, patch.element) 49 | end 50 | 51 | defp element_name(line) do 52 | case Regex.scan(@matcher, line) do 53 | [[_, name]] -> name 54 | _ -> nil 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/util.ex: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.Util do 2 | @source_matcher ~r/ (.*):(\d*)(?::|\z)/m 3 | 4 | def parse_source(line) do 5 | case Regex.scan(@source_matcher, line) do 6 | [[_, path, line_number]] -> 7 | case Integer.parse(line_number) do 8 | {line_number, _} -> 9 | {path, line_number} 10 | 11 | _e -> 12 | # TODO: check why it doesnt match 13 | nil 14 | end 15 | 16 | _ -> 17 | nil 18 | end 19 | end 20 | 21 | def prefix_with_underscore(line, var_name) do 22 | ~r/([\s\(])(#{var_name}\W)/ 23 | |> Regex.replace(line, "\\g{1}_\\g{2}") 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule FixWarnings.Mixfile do 2 | use Mix.Project 3 | 4 | @version "0.1.3" 5 | 6 | def project do 7 | [ 8 | app: :fix_warnings, 9 | version: @version, 10 | elixir: "~> 1.4", 11 | description: "A mix task that automatically fixes compiler warnings in your Elixir project", 12 | package: [ 13 | licenses: "MIT", 14 | maintainers: ["hasclass"], 15 | licenses: ["MIT"], 16 | links: %{github: "https://github.com/hasclass/fix_warnings"}, 17 | files: 18 | ~w(lib) ++ 19 | ~w(LICENSE.md mix.exs README.md) 20 | ], 21 | docs: [ 22 | source_ref: "v#{@version}", 23 | main: "Mix.Tasks.FixWarnings" 24 | ], 25 | build_embedded: Mix.env() == :prod, 26 | start_permanent: Mix.env() == :prod, 27 | deps: deps() 28 | ] 29 | end 30 | 31 | # Configuration for the OTP application 32 | # 33 | # Type "mix help compile.app" for more information 34 | def application do 35 | # Specify extra applications you'll use from Erlang/Elixir 36 | [extra_applications: [:logger]] 37 | end 38 | 39 | # Dependencies can be Hex packages: 40 | # 41 | # {:my_dep, "~> 0.3.0"} 42 | # 43 | # Or git/path repositories: 44 | # 45 | # {:my_dep, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 46 | # 47 | # Type "mix help deps" for more examples and options 48 | defp deps do 49 | [ 50 | {:ex_doc, "~> 0.16", only: :dev} 51 | ] 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "earmark": {:hex, :earmark, "1.2.2", "f718159d6b65068e8daeef709ccddae5f7fdc770707d82e7d126f584cd925b74", [:mix], [], "hexpm", "59514c4a207f9f25c5252e09974367718554b6a0f41fe39f7dc232168f9cb309"}, 3 | "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, 4 | "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, 5 | "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, 6 | "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, 7 | "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, 8 | "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, 9 | } 10 | -------------------------------------------------------------------------------- /overview.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/millllllz/fix_warnings/adb8c2f51b3560de7b978f15430efae2612766ca/overview.md -------------------------------------------------------------------------------- /test/fix_warnings_test.exs: -------------------------------------------------------------------------------- 1 | defmodule FixWarningsTest do 2 | use ExUnit.Case 3 | doctest FixWarnings 4 | 5 | test "basic test" do 6 | %{file: "test/test.log"} 7 | |> FixWarnings.changes() 8 | |> assert_changes 9 | end 10 | 11 | test "basic test for deprecated logs style (without function references)'" do 12 | %{file: "test/test_deprecated.log"} 13 | |> FixWarnings.changes() 14 | |> assert_changes 15 | end 16 | 17 | defp assert_changes(changes) do 18 | assert String.trim(changes["examples/foo.ex"]) == 19 | String.trim(""" 20 | defmodule Examples.Bar do 21 | end 22 | 23 | defmodule Examples.Foo do 24 | alias String 25 | 26 | def foo(_unused_param) do 27 | String.length("hello") 28 | end 29 | 30 | def foo(_unused_param1, _unused_param2) do 31 | end 32 | end 33 | """) 34 | end 35 | 36 | test "edge-cases" do 37 | changes = FixWarnings.changes(%{file: "test/todo.log"}) 38 | 39 | assert String.trim(changes["examples/todo.ex"]) == 40 | String.trim(""" 41 | defmodule Examples.Bar do 42 | end 43 | defmodule Examples.Baz do 44 | end 45 | 46 | defmodule Examples.AliasWithCurlyBrackets do 47 | alias Examples.{Baz} 48 | 49 | Baz 50 | 51 | def parse_headers(_headers, headers1) do 52 | headers1 53 | end 54 | 55 | def parse_params(%{"title" => _title}), do: nil 56 | def parse_params(%{title: _title}), do: nil 57 | end 58 | """) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /test/test.log: -------------------------------------------------------------------------------- 1 | Compiling 1 file (.ex) 2 | warning: variable "unused_param" is unused (if the variable is not meant to be used, prefix it with an underscore) 3 | examples/foo.ex:9: M.f/2 4 | 5 | warning: variable "unused_param1" is unused (if the variable is not meant to be used, prefix it with an underscore) 6 | examples/foo.ex:13: M.f/2 7 | 8 | warning: variable "unused_param2" is unused (if the variable is not meant to be used, prefix it with an underscore) 9 | examples/foo.ex:13: M.f/2 10 | 11 | warning: unused alias Bar 12 | examples/foo.ex:6 13 | 14 | warning: unused alias Bar 15 | examples/file_that_does_not_exist_eg_an_external_package.ex:6 16 | 17 | Generated fix_warnings app 18 | -------------------------------------------------------------------------------- /test/test_deprecated.log: -------------------------------------------------------------------------------- 1 | warning: variable "unused_param" is unused 2 | examples/foo.ex:9 3 | 4 | warning: variable "unused_param1" is unused 5 | examples/foo.ex:13 6 | 7 | warning: variable "unused_param2" is unused 8 | examples/foo.ex:13 9 | 10 | warning: unused alias Bar 11 | examples/foo.ex:6 12 | 13 | warning: unused alias Bar 14 | examples/file_that_does_not_exist_eg_an_external_package.ex:6 15 | 16 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /test/todo.log: -------------------------------------------------------------------------------- 1 | warning: variable "headers" is unused 2 | examples/todo.ex:12 3 | 4 | warning: unused alias Bar 5 | examples/todo.ex:8 6 | 7 | warning: variable "title" is unused 8 | examples/todo.ex:16 9 | 10 | warning: variable "title" is unused 11 | examples/todo.ex:17 12 | -------------------------------------------------------------------------------- /test/util_test.exs: -------------------------------------------------------------------------------- 1 | defmodule UtilTest do 2 | use ExUnit.Case 3 | doctest FixWarnings.Util 4 | 5 | alias FixWarnings.Util 6 | 7 | describe "parse_source/1" do 8 | test "parses path and line_number from source line" do 9 | assert {"examples/foo.ex", 13} == Util.parse_source(" examples/foo.ex:13: M.f/2") 10 | end 11 | 12 | test "parses path and line_number from deprecated-style source line" do 13 | assert {"examples/foo.ex", 13} == Util.parse_source(" examples/foo.ex:13") 14 | end 15 | end 16 | 17 | describe "prefix_with_underscore/2" do 18 | test "safely prefixes with _" do 19 | assert "(_bar)" == Util.prefix_with_underscore("(bar)", "bar") 20 | end 21 | 22 | test "ignores map string keys" do 23 | assert "%{\"bar\" => _bar}" == 24 | Util.prefix_with_underscore("%{\"bar\" => _bar}", "bar") 25 | end 26 | 27 | test "ignores map atom keys" do 28 | assert "%{bar: _bar}" == Util.prefix_with_underscore("%{bar: _bar}", "bar") 29 | end 30 | 31 | test "safely prefixs with my_bar" do 32 | assert "my_bar" == Util.prefix_with_underscore("my_bar", "bar") 33 | end 34 | 35 | test "safely prefixs with mybar" do 36 | assert "mybar" == Util.prefix_with_underscore("mybar", "bar") 37 | end 38 | end 39 | end 40 | --------------------------------------------------------------------------------