Skip to content

Allow more things to be marked deprecated through docstrings#1461

Draft
adhoc-bobcat wants to merge 3 commits intobazelbuild:mainfrom
adhoc-bobcat:buildifier_warnings
Draft

Allow more things to be marked deprecated through docstrings#1461
adhoc-bobcat wants to merge 3 commits intobazelbuild:mainfrom
adhoc-bobcat:buildifier_warnings

Conversation

@adhoc-bobcat
Copy link
Copy Markdown

@adhoc-bobcat adhoc-bobcat commented Apr 2, 2026

Buildtools PR checklist

  • The code in this PR is covered by unit/integration tests.
  • I have tested these changes and provide testing instructions below.
  • I have either responded to, or resolved all Gemini comments on the PR.
  • I have read Google Eng Practices on Small Changes, this PR either follows these guidelines or the description provides reasoning for why they can not be followed.

Description

This change allows module extensions and their tags, along with rules to be marked as deprecated so that buildifier emits a warning if they are used.

This is aligned with the existing function deprecation check, where the tool looks for the text "Deprecated:" in the docstring.

Module extensions and rules have two places where they can be marked deprecated.

  • When calling the global function to create the extension or rule, through a "doc" arg.
  • Through the docstring of the implementation function.

These changes allow either place to be used.

These changes were tested using the following steps

$ bazelisk test //...

This change allows module extensions to be marked as deprecated with a
docstring comment, so that buildifier emits a warning if they are used.

Module extensions can be marked as deprecated in two ways:

- When calling the `module_extension` global function to create the
  extension, through the `doc` argument
- Through the docstring of the implementation function.

If either docstring contains the text "Deprecated:", buildifier
considers the module extension deprecated, and will emit a warning.
This change allows the tag clases defined by module extensions to be
marked as deprecated with a docstring comment, so that buildifier emits
a warning if they are used.

Tag classes are marked as deprecated through the `doc` argument to the
`tag_class` global function used to create them.
This change allows rules to be marked as deprecated with a docstring
comment, so that buildifier emits a warning if they are used.

Rules are considered used when they are referenced during a `load` call
from the file where they are defined, or if they are named in a
`use_repo_rule` function call.

A rule is considered to be any of those constructed by the functions
`rule`, `repository_rule`, or `materialzied_rule`. These rules can be
marked as deprecated in two ways:

- When calling the global function to create the rule, through the `doc`
  argument
- Through the docstring of the implementation function.

If either docstring contains the text "Deprecated:", buildifier
considers the rule deprecated, and will warn if it is used.
@adhoc-bobcat adhoc-bobcat requested a review from a team as a code owner April 2, 2026 03:51
@adhoc-bobcat adhoc-bobcat requested review from oreflow and removed request for a team April 2, 2026 03:51
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new linter warnings for deprecated module extensions, module extension tags, and rules. It refactors the existing deprecation warning logic by splitting warn_deprecated.go into specialized files and adds comprehensive integration and unit tests. Documentation in WARNINGS.md and warnings.textproto has been updated to reflect these new categories. I have no feedback to provide.

@adhoc-bobcat adhoc-bobcat marked this pull request as draft April 3, 2026 04:47
@adhoc-bobcat
Copy link
Copy Markdown
Author

I realized this evening there was something important missing.

More complicated Bazel modules implement their rules and extensions privately, and use a re-export pattern, such as:

# In a public file
load("//private:impl.bzl", _impl="impl")
impl = _impl

As my changes followed the pattern set by the deprecated function warning, the buildifier running on Bazel files that used the "public" version of impl would load the small public re-export file, and decide it wasn't deprecated, since the definition there doesn't have a docstring.

(And no, I'm not proposing to fix it by recurively scanning. That's pretty obviously not how buildifier is mean to work.)

I had some thoughts for how to fix this, and they would simplify the implementation.

  1. The deprecation checks could look for an assignment to the global name of interest that looks like impl = _deprecated_impl in the loaded file. This is certainly simple, but might be a little too magical. It also requires module authors to do some renaming.

  2. The deprecation check could look for an assignment to the global name of interest that looks like impl = _any_function(_impl), where _any_function(x) is just a decorator that returns x. If the docstring to that function had the "Deprecated:" substring, then buildifier would consider the name assigned to be deprecated.

    This aligns better with how buildifier currently works to decide if a function is deprecated, because that is what it looks for.

    It would mean a slightly larger (but still small) one-time runtime cost for Bazel, as it would have to evaluate the function call operation. That said, an advantage to this approach is that module authors could define a deprecation helper like this:

    def _deprecated(x, use_instead=None):
       """Deprecated: See the use_instead string."""
       return x
    
    impl = _deprecated(_impl, use_instead="Use other_impl")

    The disadvantage is that they'd have to reimplement that function in every file they needed to declare something as deprecated, since buildifier wouldn't visit the next level of loads.

Feel free to leave any thoughts here. I'll likely go ahead and implement (2), before removing the draft status I added on this PR. (I don't think it's really that large a change if I need to switch to (1), or even some third option.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant