• This repository has been archived on 13/Aug/2020
  • Stars
    star
    859
  • Rank 52,841 (Top 2 %)
  • Language
    Haskell
  • License
    MIT License
  • Created over 8 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Deprecated; see https://github.com/unused-code/unused

Unused Build Status

A command line tool to identify unused code.

NOTICE: As of May 27, 2020, this version of Unused has been deprecated

Unused has been rewritten and now lives at https://github.com/unused-code/unused

Issues, updates, and all future work will occur there.

Image of Unused Output

"What kinds of projects can I used it on?"

Anything.

Yes, literally anything.

It's probably best if you have a file generated from ctags it can read from (it looks in .git, tmp, and the root directory for a tags file), but if you have another way to pipe a bunch of methods/functions/classes/modules/whatever in, that works too.

Right now, there are some special cases built in for Rails and Phoenix apps (specifically, assumptions about what's fine to only have one reference to, e.g. Controllers in Rails and Views in Phoenix), but it'll work on Rubygems, Elixir packages, or anything else.

That said, be confident the code you're removing won't break your program. Especially with projects built in Ruby, Elixir, or JavaScript, there are ways to dynamically trigger or define behavior that may be surprising. A test suite can help here, but still cannot determine every possible execution path.

Installing and Updating

Homebrew (Recommended)

You can install my formulae via Homebrew with brew tap:

brew tap joshuaclayton/formulae

Next, run:

brew install unused

This will install unused and its corresponding dependencies.

To update, run:

brew update
brew upgrade unused

Alternatively, you can install with Stack or by hand. Because it needs to compile, installation times may vary, but it's often several minutes.

Stack

If you already have Stack installed, ensure you have the latest list of packages:

stack update

Verify Stack is using at least lts-6.0 when installing by checking the global project settings in ~/.stack/global-project/stack.yaml.

Once that is complete, run:

stack install unused

This will install unused in the appropriate directory for Stack; you'll want to ensure your $PATH reflects this.

Installing by hand

This project is written in Haskell and uses Stack.

Once you have these tools installed and the project cloned locally:

stack setup
stack install

This will generate a binary in $HOME/.local/bin; ensure this directory is in your $PATH.

Install via Docker

Once Docker is installed, create a binary within your $PATH to run the image:

#!/usr/bin/env bash

docker run --rm -it -v $(pwd):/code joshuaclayton/unused unused $@

Note that, because Unused will be running inside of a virtual machine, it will take longer to generate output than were you to install via previously-mentioned methods.

Using Unused

unused attempts to read from common tags file locations (.git/tags, tags, and tmp/tags).

In an application where the tags file exists, run:

unused

If you don't have a tags file, you can generate one by running:

git ls-files | xargs ctags

If you want to specify a custom tags file, or load tokens from somewhere else, run:

cat .custom/tags | unused --stdin

To view more usage options, run:

unused --help

Troubleshooting

Ctags (and a corresponding workflow) isn't configured

Exuberant Ctags (or another tool that will generate a tags file, like hasktags for Haskell projects) is required to use unused correctly; however, the version of ctags that ships with OS X (/usr/bin/ctags) is an older version won't work with many languages (that BSD version of ctags says it "makes a tags file for ex(1) from the specified C, Pascal, Fortran, YACC, lex, and lisp sources.")

Installation via Homebrew includes the ctags dependency. You can also run brew install ctags by hand. If you're not on OS X, use your favorite package manager and refer to the Exuberant Ctags site for download instructions.

Ctags manual run

If you're using ctags to generate a tags file prior to running unused and don't have a workflow around automatically generating a tags file, run:

git ls-files | xargs ctags -f tmp/tags

This will take your .gitignore into account and write the tags file to tmp/tags. Be sure to write this to a location that's ignored by git.

While this process allows a developer to get started, it requires remembering to run this command before running unused. Let's explore how to automate this process.

Ctags automatic runs via git hooks

With ctags installed, you'll likely want to configure your workflow such that your tags file gets updated periodically without any action on your part. I recommend following the instructions outlined by Tim Pope on this matter, which discusses a workflow coupled to git for managing the tags file. It includes shell scripting that may not look "effortless"; however, the fact this is automated helps to ensure unused is running against new versions of the code as you (and other teammates, if you have any) are committing.

As he suggests, you'll want to run git init into the directories you want this hook, and to manually run the hook:

git ctags

unused is configured to look for a tags file in three different directories, including .git/ as the article suggests, so no further configuration will be necessary with unused.

"Calculating cache fingerprint" takes a long time

unused attempts to be intelligent at understanding if your codebase has changed before running analysis (since it can be time-consuming on large codebases). To do so, it calculates a "fingerprint" of the entire directory by using md5 (or md5sum), along with find and your .gitignore file.

If you're checking in artifacts (e.g. node_modules/, dist/, tmp/, or similar), unused will likely take significantly longer to calculate the fingerprint.

Per the --help documentation, you can disable caching with the -C flag:

$ unused -C

"No results found" when expecting results

If you're expecting to see results but unused doesn't find anything, verify that any artifacts unused uses (e.g. the tags file, wherever it's located) or generates (e.g. in PROJECT_ROOT/tmp/unused) is .gitignored.

What might be happening is, because unused searches for tokens with ag (which honors .gitignore), it's running into checked-in versions of the tokens from other files, resulting in duplicate occurrences that aren't representative of the actual codebase. The most obvious might be the tags file itself, although if you're using an IDE that runs any sort of analysis and that's getting checked in somehow, that may cause it too.

One final piece to check is the number of tokens in the tags file itself; if ctags is misconfigured and only a handful of tokens are being analyzed, they all may have low removal likelihood and not display in the default results (high-likelihood only).

Analysis takes a long time due to a large number of terms found

In my experience, projects under 100,000LOC should have at most around 8,000 unique tokens found. This obviously depends on how you structure your classes/modules/functions, but it'll likely be close.

If you're seeing more than 15,000 terms matched (I've seen upwards of 70,000), this is very likely due to misconfiguration of ctags where it includes some amount of build artifacts. In Ruby, this might be a RAILS_ROOT/vendor directory, or if you're using NPM, APP_ROOT/node_modules or APP_ROOT/bower_components.

When configuring ctags, be sure to include your --exclude directives; you can find an example here.

Custom Configuration

The first time you use unused, you might see a handful of false positives. unused will look in two additional locations in an attempt to load additional custom configuration to help improve this.

Configuration format

# Language or framework name
#   e.g. Rails, Ruby, Go, Play
- name: Framework or language
  # Collection of matches allowed to have one occurrence
  autoLowLikelihood:
    # Low likelihood match name
    - name: ActiveModel::Serializer
      # Flag to capture only capitalized names
      #   e.g. would match `ApplicationController`, not `with_comments`
      classOrModule: true

      # Matcher for `.*Serializer$`
      #   e.g. `UserSerializer`, `ProjectSerializer`
      termEndsWith: Serializer

      # Matcher for `^with_.*`
      #   e.g. `with_comments`, `with_previous_payments`
      termStartsWith: with_

      # Matcher for `^ApplicationController$`
      termEquals: ApplicationController

      # Matcher for `.*_factory.ex`
      #   e.g. `lib/appname/user_factory.ex`, `lib/appname/project_factory.ex`
      pathEndsWith: _factory.ex

      # Matcher for `^app/policies.*`
      #   e.g. `app/policies/user_policy.rb`, `app/policies/project_policy.rb`
      pathStartsWith: app/policies

      # list of termEquals
      # Matcher allowing any exact match from a list
      allowedTerms:
      - index?
      - edit?
      - create?

~/.unused.yml

The first location is ~/.unused.yml. This should hold widely-used configuration roughly applicable across projects. Here's an example of what might be present:

- name: Rails
  autoLowLikelihood:
    - name: ActiveModel::Serializer
      termEndsWith: Serializer
      classOrModule: true
    - name: Pundit
      termEndsWith: Policy
      classOrModule: true
      pathEndsWith: .rb
    - name: Pundit Helpers
      allowedTerms:
        - Scope
        - index?
        - new?
        - create?
        - show?
        - edit?
        - destroy?
        - resolve
    - name: JSONAPI::Resources
      termEndsWith: Resource
      classOrModule: true
      pathStartsWith: app/resources
    - name: JSONAPI::Resources Helpers
      allowedTerms:
      - updatable_fields
      pathStartsWith: app/resources

I tend to work on different APIs, and the two libraries I most commonly use have a fairly similar pattern when it comes to class naming. They both also use that naming structure to identify serializers automatically, meaning they very well may only be referenced once in the entire application (when they're initially defined).

Similarly, with Pundit, an authorization library, naming conventions often mean only one reference to the class name.

This is a file that might grow, but is focused on widely-used patterns across codebases. You might even want to check it into your dotfiles.

APP_ROOT/.unused.yml

The second location is APP_ROOT/.unused.yml. This is where any project-specific settings might live. If you're working on a library before extracting to a gem or package, you might have this configuration take that into account.

Validation

unused will attempt to parse both of these files, if it finds them. If either is invalid either due to missing or mistyped keys, an error will be displayed.

Requirements

Unused leverages Ag to analyze the codebase; as such, you'll need to have ag available in your $PATH. This is set as an explicit dependency in Homebrew.

Alternatively, if you'd like to use RipGrep, you can do so with the --search rg flag. Be sure to have RipGrep installed first.

Testing

To run the test suite, run:

stack test

License

Copyright 2016-2018 Josh Clayton. See the LICENSE.

More Repositories

1

blueprint-css

A CSS framework that aims to cut down on your CSS development time
CSS
5,318
star
2

dotfiles

Because everyone else is doing it
Shell
231
star
3

polylines

Easily handle Google polylines
Ruby
93
star
4

palette

An easier way to write Vim color schemes
Ruby
89
star
5

specit

A wrapper around QUnit that has a similar style to RSpec
JavaScript
71
star
6

phoenix_elm_webpack_heroku_example_app

Phoenix and Elm, compiled by Webpack, running on Heroku
Elixir
36
star
7

backbone-data-bootstrap

Example app demoing data bootstrapping with backbone
Ruby
27
star
8

page_ez

PageEz is a tool to define page objects with Capybara.
Ruby
17
star
9

fusebox

A super-lightweight lightbox-esque jQuery plugin (with jQuery UI support)
JavaScript
13
star
10

elm-ports-example

Example application demonstrating inbound/outbound ports in Elm
CSS
12
star
11

jitter

jQuery + Twitter
JavaScript
11
star
12

douglas_peucker

A Ruby implementation of the Douglas-Peucker algorithm
Ruby
11
star
13

elm-colors

Phoenix + Elm application for browsing colors
CSS
10
star
14

peas

A demo app of a URL shortener
Ruby
10
star
15

faststep

Mongo on Speed
C
10
star
16

sieve

Ruby C Extension for the Sieve of Eratosthenes
Ruby
10
star
17

file-fun

A fun program for files, written in Haskell, to demonstrate a monad transformer stack
Haskell
7
star
18

todos

Ruby
7
star
19

date-math

A small parser and CLI for date calculations
Rust
7
star
20

google-analytics-elm-example

Elm
6
star
21

quizzical

Elm
5
star
22

basic_decorator

Ruby
5
star
23

Write-Web-Applications-Your-Customers-Want

GLRB Talk
JavaScript
4
star
24

elm-hop-mailboxes-example

Elm
4
star
25

possible_unused_methods

Identify possible unused methods/functions in a codebase
Elixir
4
star
26

qunit-examples

JavaScript
4
star
27

sentinel

Simple authorization for Rails (includes Shoulda macros)
Ruby
4
star
28

rails_elm_webpacker_heroku_example_app

Rails and Elm, compiled by Webpack, running on Heroku
Ruby
4
star
29

new-test-driven-todos

Ruby
3
star
30

import-sort

Sort your Haskell import statements
Haskell
3
star
31

watchtower

An exception logger for Rails 2.3
JavaScript
3
star
32

moving-averages

Haskell
3
star
33

j-archive-api

Haskell
3
star
34

json-diff

Rust
3
star
35

ship-it

Elm
2
star
36

todo

Todo terminal app written in Haskell
Haskell
2
star
37

jdclayton

Personal blog
Ruby
2
star
38

engineering-skills-matrix

Elm
2
star
39

investments-elm

A little can turn into a lot.
CSS
2
star
40

redirect-to

Elixir
2
star
41

advent-of-code-rust

Rust
2
star
42

chords

Haskell
2
star
43

scarab

Scrabble word finder
Ruby
2
star
44

alfred-freshbooks

Ruby
2
star
45

date-calculations

Rust Crate supporting relative date calculations for Chrono's NaiveDate
Rust
2
star
46

dots_web

JavaScript
2
star
47

exploring-elm

Elm
2
star
48

fuzzy-monster

Demo app utilizing recent standard programming practices, as well as a place to try out Rails 2.2 RC1 and any plugins
JavaScript
2
star
49

testing_rails

Ruby
2
star
50

token-search

This is a library for efficient substring detection across a codebase.
Ruby
2
star
51

geocoding

Elixir
1
star
52

colorcoded-words

Haskell
1
star
53

grammarly-bug

JavaScript
1
star
54

advent-of-code-2016

Haskell
1
star
55

math-equations-csv

Rust
1
star
56

polygon.js

Quick and dirty Polygon object in Javascript
JavaScript
1
star
57

advent-of-code

Haskell
1
star
58

hashtag

Ruby
1
star
59

timeline

Haskell
1
star
60

hangman

Hangman exercise from http://haskellbook.com/
Haskell
1
star
61

similar-names

Haskell
1
star
62

j-archive-parser

Haskell
1
star
63

multi_step_component

Ruby
1
star