JakeBecker / elixir-ls

A frontend-independent IDE "smartness" server for Elixir. Implements the JSON-based "Language Server Protocol" standard and provides debugger support via VS Code's debugger protocol.
Apache License 2.0
846 stars 52 forks source link

Option to wipe and rerun dialyzer analysis #169

Closed PragTob closed 4 years ago

PragTob commented 5 years ago

:wave:

First, thanks a lot for this great project. It has made my elixir and dialyzer coding much more pleasant :clap: :clap: :clap:

Something that I've noticed though is that sometimes it seems to get stuck on old dialyzer information and then show me bad errors.

for instance:

``` The call 'Elixir.BBEngine.Simulation':jump_ball (#{'__struct__' := 'Elixir.BBEngine.GameState', 'ball_handler_id' := 'nil', 'box_score' := #{'__struct__' := 'Elixir.BBEngine.BoxScore', 'clock_seconds' := 600, 'home' := #{'team' => #{'total' := #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}, pos_integer() => #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}}, integer() => #{'total' := #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}, pos_integer() => #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}}}, 'quarter' := 1, 'road' := #{'team' => #{'total' := #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}, pos_integer() => #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}}, integer() => #{'total' := #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}, pos_integer() => #{'__struct__' := 'Elixir.BBEngine.BoxScore.Statistics', 'blocked_shots' := non_neg_integer(), 'blocks' := non_neg_integer(), 'defensive_rebounds' := non_neg_integer(), 'field_goals_attempted' := non_neg_integer(), 'field_goals_made' := non_neg_integer(), 'fouls' := non_neg_integer(), 'offensive_rebounds' := non_neg_integer(), 'points' := non_neg_integer(), 'rebounds' := non_neg_integer(), 'steals' := non_neg_integer(), 'three_points_attempted' := non_neg_integer(), 'three_points_made' := non_neg_integer(), 'turnovers' := non_neg_integer(), 'two_points_attempted' := non_neg_integer(), 'two_points_made' := non_neg_integer()}}}, 'shot_clock' := 24}, 'current_seed' := {#{'next' := fun((_) -> {non_neg_integer(), _}), 'type' := atom(), 'bits' => non_neg_integer(), 'jump' => fun(({#{'type' := _, _ => _}, _}) -> {#{'type' := _, _ => _}, _}), 'max' => non_neg_integer(), 'uniform' => fun(({#{'type' := _, _ => _}, _}) -> {float(), {#{'type' := _, _ => _}, _}}), 'uniform_n' => fun((pos_integer(), {#{'type' := _, _ => _}, _}) -> {pos_integer(), {#{'type' := _, _ => _}, _}}), 'weak_low_bits' => non_neg_integer()}, _}, 'events' := [], 'home' := #{'__struct__' := 'Elixir.BBEngine.Squad', 'bench' := [integer()], 'lineup' := [integer()], 'players' := [#{'__struct__' := 'Elixir.BBEngine.Player', 'defensive_rating' := number(), 'defensive_rebound' := number(), 'id' := integer(), 'offensive_rating' := number(), 'offensive_rebound' := number(), 'team' := 'home' | 'road'}]}, 'initial_seed' := {#{'next' := fun((_) -> {non_neg_integer(), _}), 'type' := atom(), 'bits' => non_neg_integer(), 'jump' => fun(({#{'type' := _, _ => _}, _}) -> {#{'type' := _, _ => _}, _}), 'max' => non_neg_integer(), 'uniform' => fun(({#{'type' := _, _ => _}, _}) -> {float(), {#{'type' := _, _ => _}, _}}), 'uniform_n' => fun((pos_integer(), {#{'type' := _, _ => _}, _}) -> {pos_integer(), {#{'type' := _, _ => _}, _}}), 'weak_low_bits' => non_neg_integer()}, _}, 'matchups' := #{integer() => integer()}, 'players' := #{integer() => #{'__struct__' := 'Elixir.BBEngine.Player', 'defensive_rating' := number(), 'defensive_rebound' := number(), 'id' := integer(), 'offensive_rating' := number(), 'offensive_rebound' := number(), 'team' := 'home' | 'road'}}, 'possession' := 'nil', 'road' := #{'__struct__' := 'Elixir.BBEngine.Squad', 'bench' := [integer()], 'lineup' := [integer()], 'players' := [#{'__struct__' := 'Elixir.BBEngine.Player', 'defensive_rating' := number(), 'defensive_rebound' := number(), 'id' := integer(), 'offensive_rating' := number(), 'offensive_rebound' := number(), 'team' := 'home' | 'road'}]}}) will never return since it differs in the 1st argument from the success typing arguments: (#{'__struct__' := 'Elixir.BBEngine.GameState', 'ball_handler_id' := 'nil' | integer(), 'box_score' := #{'__struct__' := 'Elixir.BBEngine.BoxScore', 'home' := #{'team' => map(), integer() => map()}, 'road' := #{'team' => map(), integer() => map()}}, 'clock_seconds' := integer(), 'current_seed' := {#{'next' := fun((_) -> any()), 'type' := atom(), 'bits' => non_neg_integer(), 'jump' => fun((_) -> any()), 'max' => non_neg_integer(), 'uniform' => fun((_) -> any()), 'uniform_n' => fun((_, _) -> any()), 'weak_low_bits' => non_neg_integer()}, _}, 'events' := [#{'__struct__' := 'Elixir.BBEngine.Event.Block' | 'Elixir.BBEngine.Event.BlockedShotRecovery' | 'Elixir.BBEngine.Event.DeflectedOutOfBounds' | 'Elixir.BBEngine.Event.Pass' | 'Elixir.BBEngine.Event.Rebound' | 'Elixir.BBEngine.Event.Shot' | 'Elixir.BBEngine.Event.Steal' | 'Elixir.BBEngine.Event.ThrowIn' | 'Elixir.BBEngine.Event.TimeRanOut' | 'Elixir.BBEngine.Event.Turnover', 'duration' := non_neg_integer(), 'actor_id' => integer(), 'blocked_player_id' => integer(), 'defender_id' => integer(), 'points' => 1 | 2 | 3, 'receiver_id' => integer(), 'stolen_from' => integer(), 'success' => boolean(), 'team' => 'home' | 'road', 'to_player' => integer(), 'to_team' => 'home' | 'road', 'type' => 'clock_violation' | 'defensive' | 'midrange' | 'offensive' | 'out_of_bound_pass' | 'three_point' | 'threepoint' | 'two_point'}], 'home' := #{'__struct__' := 'Elixir.BBEngine.Squad', 'bench' := [integer()], 'lineup' := [integer()], 'players' := [map()]}, 'initial_seed' := {#{'next' := fun((_) -> any()), 'type' := atom(), 'bits' => non_neg_integer(), 'jump' => fun((_) -> any()), 'max' => non_neg_integer(), 'uniform' => fun((_) -> any()), 'uniform_n' => fun((_, _) -> any()), 'weak_low_bits' => non_neg_integer()}, _}, 'matchups' := #{integer() => integer()}, 'players' := #{integer() => #{'__struct__' := 'Elixir.BBEngine.Player', 'defensive_rating' := number(), 'defensive_rebound' := number(), 'id' := integer(), 'offensive_rating' := number(), 'offensive_rebound' := number(), 'team' := 'home' | 'road'}}, 'possession' := 'home' | 'nil' | 'road', 'quarter' := pos_integer(), 'road' := #{'__struct__' := 'Elixir.BBEngine.Squad', 'bench' := [integer()], 'lineup' := [integer()], 'players' := [map()]}, 'shot_clock' := non_neg_integer()}) ```

Checking this you'll find that "quarter" appears in both versions but the success typing has it nested under game state while the first has it nested under box_score. I had moved it from game_state to box_score and this made the in editor analysis go wild. My local dialyxir run still reported everything as being fine (which is good) and continues to do so. It should run with the same set of warnings etc.. Wiping some files in .elixir_ls (manifest and dialyzer temp) along with adding code to some files and saving has remedied the problem, but I think it'd be great if there was an action like "nuke and rebuild all this" as an easy first fix.

Happy to help if this is something you want to have.

Thanks again for all your work and effort :green_heart:

Cheers, Tobi

edit: for whatever reason my workaround doesn't seem to be fool proof, 3 files still have a similar error that combinations of rm -rf .elixir_ls, editor relaunching and wild saving don't seem to be able to get rid off :'(

JakeBecker commented 5 years ago

I've encountered this too, but it's difficult to reproduce on demand. Next time it happens, can you grab the dialyzer_manifest from .elixir_ls and upload it somewhere and post the file, module, and line with the error that won't go away? That might help. Thanks!

PragTob commented 5 years ago

:wave:

Thanks for getting back to me! Totally will. I agree that fixing the root cause is probably better - but even in fancy java IDEs I often needed a kill all button so do you think it's still worth considering?

For future people, I did manage to resolve my above issue rm -rfing the .elixir_ls folder and restarting vscode.

Thanks a bunch! :green_heart:

PragTob commented 5 years ago

:wave:

Me again, so a similar case just manifested - albeit quite a different error but at its root it's the same: Have a persisting dialyzer error in the project that I can't seem to get rid off but my manual dialyzer run passes.

Manifest file: https://www.dropbox.com/s/kkyot2xlm2tovqt/dialyzer_manifest_22.0_elixir-1.8.2_test?dl=0

Project at this point in time: https://github.com/PragTob/bb_engine/tree/f74afb44aaa8435ca311b8e9a2e3f3ac35be655b

Error message

The pattern 
          #{'free_throws_remaining' := _remaining@1,
            '__struct__' := 'Elixir.BBEngine.Event.FreeThrow'} can never match the type 
          #{'__struct__' :=
                'Elixir.BBEngine.Event.Block' |
                'Elixir.BBEngine.Event.BlockedShotRecovery' |
                'Elixir.BBEngine.Event.DeflectedOutOfBounds' |
                'Elixir.BBEngine.Event.EndOfQuarter' |
                'Elixir.BBEngine.Event.Foul' |
                'Elixir.BBEngine.Event.GameFinished' |
                'Elixir.BBEngine.Event.Pass' |
                'Elixir.BBEngine.Event.Rebound' |
                'Elixir.BBEngine.Event.Shot' |
                'Elixir.BBEngine.Event.Steal' |
                'Elixir.BBEngine.Event.ThrowIn' |
                'Elixir.BBEngine.Event.Turnover',
            'duration' := non_neg_integer(),
            'actor_id' => integer(),
            'blocked_player_id' => integer(),
            'defender_id' => integer(),
            'during_shot' => boolean(),
            'fouled_player_id' => integer(),
            'points' => 1 | 2 | 3,
            'receiver_id' => integer(),
            'stolen_from' => integer(),
            'success' => boolean(),
            'team' => 'home' | 'road',
            'to_player' => integer(),
            'to_team' => 'home' | 'road',
            'type' =>
                'clock_violation' | 'defensive' | 'midrange' |
                'offensive' | 'out_of_bound_pass' | 'three_point' |
                'threepoint' | 'two_point'}

As you can see, the error is mostly about Elixir.BBEngine.Event.FreeThrow not being allowed as a struct here.

Code where error appears

Code where this comes from, but simplified:

  @spec play(GameState.t()) :: {GameState.t(), Event.FreeThrow.t()}
  def play(game_state) do
    [last_event | _] = game_state.events
    remaining(last_event)
    # ....
  end

  # error is on this function
  defp remaining(%Event.FreeThrow{free_throws_remaining: remaining}),
    do: remaining - 1

Which should be fine as the GameState type is as follows:

  @type t :: %__MODULE__{
          ....
          events: [BBEngine.Event.t()]
        }

And the event type is:

  @type t ::
          Event.Rebound.t()
          | Event.Shot.t()
          | Event.ThrowIn.t()
          | Event.Pass.t()
          | Event.Turnover.t()
          | Event.Steal.t()
          | Event.Block.t()
          | Event.Foul.t()
          | Event.BlockedShotRecovery.t()
          | Event.DeflectedOutOfBounds.t()
          | Event.EndOfQuarter.t()
          | Event.GameFinished.t()
          | Event.FreeThrow.t()

Others/circumstances:

Anyhow, thanks a bunch!

axelson commented 5 years ago

@PragTob one thing that doesn't seem likely in your case but can cause differences in output from elixir-ls's dialyzer run and a manual run is that elixir-ls runs dialyzer with MIX_ENV=test, so you could try a manual run in that env.

PragTob commented 5 years ago

@axelson yeah I thought that was the case way when I began (and once it was because the error was related to some support test file) - not this time around though. Thanks for the tip though :)

JakeBecker commented 4 years ago

This project has moved!

It's now being maintained by proactive volunteers from the Elixir community over at elixir-lsp/elixir-ls. Updates will continue to be published from that repo to the original VS Code extension, so no need to switch plugins if you're using VS Code.

To avoid inundating the new maintainers with issues, please verify that your issue persists with the latest version of the extension (which is published from the new repo) before re-filing your issue there.

Thanks for using ElixirLS!