Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find Unused Code #20

Open
JuanitoFatas opened this issue Jun 30, 2016 · 19 comments

Comments

Projects
None yet
5 participants
@JuanitoFatas
Copy link
Member

commented Jun 30, 2016

We will use a CLI tool to find unused code automatically 馃攷馃攷馃攷.

Unused

Unused is a CLI tool to find unused code, written in 100% Haskell by awesome Josh Clayton.

Install Unused on macOS

$ brew tap joshuaclayton/formulae
$ brew install unused

Ctags for macOS

Install Ctags via homebrew instead of using system built-in older Ctags:

$ brew install ctags

If you having problem that your Ctags does not load homebrew-installed one, add an alias (zsh):

alias ctags="`brew --prefix`/bin/ctags"

Generate index file

For Ruby files here is an example:

$ ctags -f .git/tags -R $(git ls-files | grep .rb)

A .tags file under .git folder will be generated, which contains the index of your Ruby code. Don't forget to gitignore your tags file.

Find Unused Code

$ unused -s -g none
* -s,--single-occurrence   Display only single occurrences
* -g,--group-by ARG        [Allowed: directory, term, file, none] Group results

unused will do the hard work, find where unused code are, and report to you:

BulkCustomerDashboard                     1, 1  app/dashboards/bulk_customer_dashboard.rb  used once
UserSerializer                            1, 1  app/serializers/user_serializer.rb         used once
authorized_repos                          1, 1  lib/github_api.rb                          used once
create_subscription_record                1, 1  app/services/repo_subscriber.rb            used once
excluded_file?                            1, 1  lib/ext/scss-lint/config.rb                used once
has_something?                            1, 1  spec/models/linter/ruby_spec.rb            used once
register_email                            1, 1  spec/models/linter/ruby_spec.rb            used once
stub_commit                               1, 1  spec/services/build_runner_spec.rb         used once
stub_customer_with_discount_find_request  1, 1  spec/support/helpers/stripe_api_helper.rb  used once
stubbed_style_checker_with_config_file    1, 1  spec/services/build_runner_spec.rb         used once
token=                                    1, 1  app/models/user.rb                         used once
user_names                                1, 1  spec/models/linter/ruby_spec.rb            used once
verified_request?                         1, 1  app/controllers/application_controller.rb  used once

Wow, unused code found, delete them and then sent a Pull Request!

Happy Housekeeping! 馃彙

Note that Unused is meant to guide, though, not give definitive answers to what can be removed.
-- Josh Clayton 鈥廆joshuaclayton

@Aqualon

This comment has been minimized.

Copy link

commented Jun 30, 2016

Thanks for this HowTo! One remark, for me the definition of alias for ctags was not necessary, since brew automatically linked it correctly.

@joshuaclayton

This comment has been minimized.

Copy link

commented Jun 30, 2016

@JuanitoFatas nice post! A couple things:

  • if the ctags file exists at .git/tags, tags, or tmp/tags within the repo, unused will load it automatically, meaning you won't have to pipe it in, so all that'd be needed is to run unused
  • -s will filter so only single-occurrence tokens are displayed, allowing you to omit piping to grep
  • -g none will also remove the grouping, so if you want individual lines without headings, you can disable those as well (a side-effect of using grep)
@Aqualon

This comment has been minimized.

Copy link

commented Jun 30, 2016

@joshuaclayton for me using tags file doesn't work. I use ctags -f tags -R $(git ls-files | grep .rb) and then call unused and it does not find anything. Weird thing is, if I use the cat $file | unused --stdin way, it works fine for a tags file starting with a ., but for files without I just get "Unused found no results". If I move the file outside of my project directory and cat it from there, it also works.

Any clue what's going on there? Running on OS X 10.11.5. btw.

@joshuaclayton

This comment has been minimized.

Copy link

commented Jun 30, 2016

@Aqualon interesting; if you cd into the project directory (where you're running unused) and run cat tags | wc -l, what is returned?

When unused can't find a tags file, it'll tell you (screenshot below). If it's saying there aren't results, it's often that the file exists but doesn't contain much/anything (often due to misconfiguration from ctags).

tmux

Let me know if anything looks off in the generated tags file - that'd be my first guess.

@Aqualon

This comment has been minimized.

Copy link

commented Jun 30, 2016

@joshuaclayton it finds 1823 lines and for both cases (the .tags file piped into unused and just having the same file as tags) I get the output Unused: analyzing 919 terms.

I think I have an idea what could be going on. Can it be that unused scans all files in the project directory again and without tags file in .gitignore it scans it too and thus gets confused? After I added it to .gitignore unused works. And that's why it maybe also works, if the file is outside the project directory and not inside.

@joshuaclayton

This comment has been minimized.

Copy link

commented Jun 30, 2016

@Aqualon yes, that'd probably do it; a tags file is (as far as I've ever known) always something you'd want git to ignore, since each developer may have different ways of generating tags on their machine.

It might be good for unused to count the tokens, and if it's non-zero, issue a message if nothing comes up - thoughts?

For projects with literally no unused code, it might be confusing - but for all others, it might be helpful to provide insight as to why nothing's showing up.

@askl56

This comment has been minimized.

Copy link

commented Jul 1, 2016

@JuanitoFatas @joshuaclayton what would be the alias option for a fish shell?

@joshuaclayton

This comment has been minimized.

Copy link

commented Jul 1, 2016

@askl56 if your $PATH is configured correctly (e.g. /usr/local/bin has higher priority than /usr/bin) you shouldn't have to write an alias at all.

That said, https://fishshell.com/docs/current/commands.html#alias looks like a good place to start (I haven't used fish myself)

@askl56

This comment has been minimized.

Copy link

commented Jul 1, 2016

Nah I know about aliases, the problem is that the ` syntax doesn't work in fish (within the quotes)

@joshuaclayton

This comment has been minimized.

Copy link

commented Jul 1, 2016

@askl56 ah - I'd check $PATH first, packages installed with Homebrew should take precedence over any system-installed binaries.

@JuanitoFatas the alias line seems confusing and totally unnecessary when $PATH is configured correctly; thoughts on removing that bit of the post too?

@askl56

This comment has been minimized.

Copy link

commented Jul 1, 2016

So @joshuaclayton what would be the command without the alias?

@joshuaclayton

This comment has been minimized.

Copy link

commented Jul 1, 2016

@askl56 The alias is necessary if your $PATH is misconfigured. which ctags should return the Homebrew-installed version. If it does, nothing else is required and you can ignore the alias section entirely. If it doesn't, follow Homebrew's instructions to configure your $PATH correctly.

@JuanitoFatas

This comment has been minimized.

Copy link
Member Author

commented Jul 1, 2016

@JuanitoFatas nice post! A couple things:

if the ctags file exists at .git/tags, tags, or tmp/tags within the repo, unused will load it automatically, meaning you won't have to pipe it in, so all that'd be needed is to run unused
-s will filter so only single-occurrence tokens are displayed, allowing you to omit piping to grep
-g none will also remove the grouping, so if you want individual lines without headings, you can disable those as well (a side-effect of using grep)

Thanks for the tips, I edited the post, much appreciated!

@JuanitoFatas

This comment has been minimized.

Copy link
Member Author

commented Jul 1, 2016

The alias is necessary if your $PATH is misconfigured. which ctags should return the Homebrew-installed version. If it does, nothing else is required and you can ignore the alias section entirely. If it doesn't, follow Homebrew's instructions to configure your $PATH correctly.

@joshuaclayton After fixed my $PATH, I don't need the alias, too. Thanks! 馃憤

@Aqualon

This comment has been minimized.

Copy link

commented Jul 1, 2016

@joshuaclayton maybe a hint in the readme which files are ignored by default (e.g. to me it looks like all dot-files are not scanned) and that the tags file should be in .gitignore? Programmatically you could check for the default run, if the found tags file is not ignored by git and thus could lead to not finding things? (I'm quite new to the whole ctags thing, so don't know what others would expect).

@padi

This comment has been minimized.

Copy link

commented Jul 21, 2016

@JuanitoFatas good post! I'm currently trying this out on a number of ruby codebases and ran into this Github issue/post. I realize that this might be a dumb question, but just for curiosity's sake: what do the 2 number columns mean (e.g. 1,1)? They sometimes differ but at least one of them refers to the number of occurrences of the class/method, but I don't know what the other one is for.

@joshuaclayton nice tool btw!

@JuanitoFatas

This comment has been minimized.

Copy link
Member Author

commented Jul 21, 2016

this might be a dumb question

This is a good question! 馃槉

what do the 2 number columns mean (e.g. 1,1)?

The first 1 is how many times it occurs, second 1 is how many matches.

For example, in my project, I ran unused, I had this one from output:

fetch!  2, 3  spec/models/rubygem_spec.rb  only the definition and corresponding tests exist

Then I do a search fetch! in my editor (against .rb files only, since I only created ctags for .rb files), the result is:

3 matches across 2 files.

Hope this helps.

@joshuaclayton

This comment has been minimized.

Copy link

commented Jul 21, 2016

@JuanitoFatas @padi spot on!

I display the numbers because there are times where there are multiple occurrences but it's still flagged as high-likelihood for removal (e.g. an instance method where the only use looks to be in the unit tests).

@padi I'd love to hear any thoughts on how to make this more clear; interested in opening an issue?

@padi

This comment has been minimized.

Copy link

commented Jul 21, 2016

So, does the occurring mean in how many files does the tag/method/class occur, regardless of how many times it appeared on the same file?

@JuanitoFatas now that I remember, long time no see from rubyconf.ph.
@joshuaclayton thanks. I made an issue as a point of discussion. joshuaclayton/unused#60

jstuckey added a commit to jstuckey/Nice-Time-on-Ice that referenced this issue Oct 11, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can鈥檛 perform that action at this time.