A structured way of embedding source analyzer metadata in Hack

v0.2.0 2024-01-14 09:18 UTC

This package is auto-updated.

Last update: 2024-05-08 07:58:21 UTC


A structured way of embedding source analyzer metadata in Hack.

How to use

// You must use use statements in exactly this form.
// `use namespace` or use statements with an `as` are not allowed.
use type HTL\Pragma\Pragmas;
use function HTL\Pragma\pragma;

// Apply an effect to an entire file.
<<file: Pragmas(vec['SourceAnalyzer', 'strict_mode=1'])>>

// Apply an effect to a class.
<<Pragmas(vec['PerfAnalyzer', 'hot_code=0'], vec['Ownership', 'maintainer=You'])>>
final class Example implements Printable {
  public function print(): string {
    // Apply an effect to the next line.
    pragma('CodingStandards', 'refused_bequest=ignore');
    throw new Exception();

For details on the strings passed to pragma(...) and Pragmas(vec[...]), read the documentation of the source analyzer.

For implementers

This package contains one class and one function:

  • The pragma function, also known as the pragma directive.
  • The Pragmas class, also known as the Pragmas attribute.

They are general replacements for structured comments.

// @lint-ignore[unused-pure-expression]
1 + 1;

@lint-ignore isn't a normal comment from the perspective of some tool. It suppresses a lint error from being raised for an unused pure expression.

I subscribe wholehartedly to the following quote from the CppCoreGuidelines:

Compilers don’t read comments ... and neither do many programmers (consistently).

If over half of the comments in a project are to silence some tool, be it the Hack typechecker or a linter, programmers will be even more inclined to skip reading the comments. It will instill fear for removing useless comments, since some tool might rely on them.

The idea behind the pragma(...) directive is to eliminate these comments. The following code block expersses the same intent.

pragma('LinterFramework', 'ignore:unused-pure-expression');
1 + 1;

Q: Why would this string-based system be better than actual attributes?

  • A: If your source code analyzer is a dev-dependency, you'd need to either:
    • Publish the attributes in a different package, to make production code typecheck without the source analyzer in the vendor/ directory.
    • Deploy the source analyzer code to production.
  • The first user of this standard is PhaLinters This is a dev-dependency with a single directive at the time of writing. The pragma(...) directive is preferred over the Pragmas annotation. By shipping pragma as standalone, PhaLinters can be a proper dev-dependency.

If you wish to accept pragma(...) directives and <<Pragmas(vec[...])>> attributes in your own source analyzers, read pragma.hack for the details for this informal standard.