/require-javadoc

Requires a Javadoc comment to be present on every Java construct (class, method, etc.)

Primary LanguageJavaMIT LicenseMIT

require-javadoc

This program requires that a Javadoc comment be present on every Java class, constructor, method, and field. It does not require a Javadoc comment on methods with an @Override annotation, nor on fields named serialVersionUID.

This tool makes no requirement about the Javadoc comment, beyond its existence. For example, this tool does not require the existence of Javadoc tags such as @param, @return, etc. You can use Javadoc itself to enforce such a requirement, but Javadoc before JDK 18 does not warn about completely missing comments. In JDK 18+, Javadoc's warnings about missing comments are not as customizable as this tool is.

Use

Example usage, to check every .java file in or under the current directory:

java -cp require-javadoc-all.jar org.plumelib.javadoc.RequireJavadoc

Details about invoking the program:

Usage: java org.plumelib.javadoc.RequireJavadoc [options] [directory-or-file ...]
  --exclude=<regex>                - Don't check files or directories whose pathname matches the regex
  --dont-require=<regex>           - Don't report problems in Java elements whose name matches the regex
  --dont-require-private=<boolean> - Don't report problems in elements with private access [default: false]
  --dont-require-noarg-constructor=<boolean> - Don't report problems in constructors with zero formal params [default: false]
  --dont-require-trivial-properties=<boolean> - Don't report problems about trivial getters and setters [default: false]
  --dont-require-type=<boolean>    - Don't report problems in type declarations [default: false]
  --dont-require-field=<boolean>   - Don't report problems in fields [default: false]
  --dont-require-method=<boolean>  - Don't report problems in methods and constructors [default: false]
  --require-package-info=<boolean> - Require package-info.java file to exist [default: false]
  --relative=<boolean>             - Report relative rather than absolute filenames [default: false]
  --verbose=<boolean>              - Print diagnostic information [default: false]

If an argument is a directory, each .java file in it or its subdirectories will be processed.

With no arguments, require-javadoc processes all the .java files in the current directory or any subdirectory.

The --dont-require regex is matched against full package names and against simple (unqualified) names of classes, constructors, methods, and fields.

A constructor with zero arguments is sometimes called a "default constructor", though that term means a no-argument constructor that the compiler synthesized when the programmer didn't write one.

All boolean options default to false, and you can omit the =<boolean> to set them to true, for example just --verbose.

With --dont-require-trivial-properties, no warnings are issued for code of the following form:

public SomeType getFoo() {
    return foo;
}

public void setFoo(SomeType foo) {
    this.foo = foo;
}

public boolean isBar() {
    return bar;
}

public boolean notBar() {
    return !bar;
}

public boolean hasBaz() {
    return baz;
}

Incremental use

In continuous integration job (Azure Pipelines, CircleCI, GitHub Actions, or Travis CI), you can require Javadoc on all changed lines and lines adjacent to changed lines. This is a way to incrementally get your code documented, without having to document it all at once. Here are example commands. (They obtain and use a program ci-lint-diff, which is part of the plume-scripts package.)

if [ -d "/tmp/$USER/plume-scripts" ] ; then
  git -C /tmp/$USER/plume-scripts pull -q 2>&1
else
  mkdir -p /tmp/$USER && git -C /tmp/$USER/ clone --filter=blob:none -q https://github.com/plume-lib/plume-scripts.git
fi
(./gradlew requireJavadoc > /tmp/warnings.txt 2>&1) || true
/tmp/$USER/plume-scripts/ci-lint-diff /tmp/warnings.txt

Gradle target

To create a requireJavadoc target, add the following to build.gradle:

configurations {
  requireJavadoc
}
dependencies {
  requireJavadoc "org.plumelib:require-javadoc:1.0.6"
}
task requireJavadoc(type: JavaExec) {
  group = 'Documentation'
  description = 'Ensures that Javadoc documentation exists.'
  mainClass = "org.plumelib.javadoc.RequireJavadoc"
  classpath = configurations.requireJavadoc
  args "src/main/java"
}
check.dependsOn requireJavadoc

You can supply other command-line arguments as well; for example:

  ...
  args "src/main/java", "--dont-require=WeakHasherMap|WeakIdentityHashMap"

Comparison to javadoc -Xwerror -Xdoclint:all

Before JDK 18, neither require-javadoc, nor javadoc -Xwerror -Xdoclint:all, nor javadoc -private -Xwerror -Xdoclint:all, is stronger. After JDK 18, require-javadoc is more configurable. Therefore, you may want to use all three.

  • require-javadoc requires that a Javadoc comment is present, but does not check the content of the comment. For example, require-javadoc does not complain if an @param or @return tag is missing.
  • Before JDK 18, Javadoc warns about problems in existing Javadoc, but does not warn if a method is completely undocumented. With -private, it checks private members too. Without -private, it ensures that public members don't reference private members. Javadoc will complain about missing @param and @return tags, but not if @Override is present. Javadoc does not warn about all Java constructs; for example, it does not process methods within enum constants, nor some private nested classes (even when -private is supplied).
  • Starting in JDK 18, javadoc -Xdoclint:all produces error messages about missing Javadoc comments. This reduces the need for the require-javadoc program. The require-javadoc program is still useful for people who:
    • are using JDK 17 or earlier
    • desire finer-grained control over which program elements must be documented. -Xdoclint provides only the key -missing, which is very coarse. The ci-lint-diff program is still useful for everyone. require-javadoc never requires comments on a default constructor, which does not appear in source code, but javadoc -Xdoclint:all does, reporting "warning: use of default constructor, which does not provide a comment". To avoid such warnings, you can run javadoc with -Xdoclint:all,-missing and rely on require-javadoc to warn about missing comments (but not about missing Javadoc tags such as @param and @return).

If you want to require all Javadoc tags to be present (a stronger requirement than require-javadoc enforces), use the Javadoc tool itself. From the command line: javadoc -private -Xwerror -Xdoclint:all (You should run with and without -private, since they yield different warnings. You may wish to adjust the -Xdoclint argument.)

In a Gradle buildfile, use one of the following (and a similar version to check only public members):

// Turn Javadoc warnings into errors, use strict checking, and process private members.
javadoc {
  options.addStringOption('Xwerror', '-Xdoclint:all')
  options.addStringOption('private', '-quiet')
}
check.dependsOn javadoc

or

task javadocStrict(type: Javadoc) {
  group = 'Documentation'
  description = 'Run Javadoc in strict mode: with -Xdoclint:all and -Xwerror, on all members.'
  source = sourceSets.main.allJava
  classpath = sourceSets.main.runtimeClasspath
  options.addStringOption('Xwerror', '-Xdoclint:all')
  options.memberLevel = JavadocMemberLevel.PRIVATE
}
check.dependsOn javadocStrict

Comparison to Checkstyle

Checkstyle is configurable to produce the same warnings as require-javadoc does.

Checkstyle has some problems:

  • Checkstyle is heavyweight to run and configure: configuring it requires multiple files.
  • Checkstyle is nondeterministic: it gives different results for file A.java depending on what other files are being checked.
  • Checkstyle crashes on correct code. For example, see checkstyle/checkstyle#5989, which the maintainers closed as "checkstyle does not support it by design".

By contrast to Checkstyle, require-javadoc is easier to use, and it actually works.