Add support for attribute docstring in the semantic model (#11315)

## Summary

This PR adds updates the semantic model to detect attribute docstring.

Refer to [PEP 258](https://peps.python.org/pep-0258/#attribute-docstrings) 
for the definition of an attribute docstring.

This PR doesn't add full support for it but only considers string
literals as attribute docstring for the following cases:
1. A string literal following an assignment statement in the **global
scope**.
2. A global class attribute

For an assignment statement, it's considered an attribute docstring only
if the target expression is a name expression (`x = 1`). So, chained
assignment, multiple assignment or unpacking, and starred expression,
which are all valid in the target position, aren't considered here.

In `__init__` method, an assignment to the `self` variable like `self.x = 1`
is also a candidate for an attribute docstring. **This PR does not
support this position.**

## Test Plan

I used the following source code along with a print statement to verify
that the attribute docstring detection is correct.

Refer to the PR description for the code snippet.

I'll add this in the follow-up PR
(https://github.com/astral-sh/ruff/pull/11302) which uses this method.
This commit is contained in:
Dhruv Manilawala 2024-05-10 20:27:56 +05:30 committed by GitHub
parent 35ba3c91ce
commit f79c980e17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 141 additions and 19 deletions

View file

@ -1643,9 +1643,17 @@ impl<'a> SemanticModel<'a> {
.intersects(SemanticModelFlags::TYPE_CHECKING_BLOCK)
}
/// Return `true` if the model is in a docstring.
pub const fn in_docstring(&self) -> bool {
self.flags.intersects(SemanticModelFlags::DOCSTRING)
/// Return `true` if the model is in a docstring as described in [PEP 257].
///
/// [PEP 257]: https://peps.python.org/pep-0257/#what-is-a-docstring
pub const fn in_pep_257_docstring(&self) -> bool {
self.flags.intersects(SemanticModelFlags::PEP_257_DOCSTRING)
}
/// Return `true` if the model is in an attribute docstring.
pub const fn in_attribute_docstring(&self) -> bool {
self.flags
.intersects(SemanticModelFlags::ATTRIBUTE_DOCSTRING)
}
/// Return `true` if the model has traversed past the "top-of-file" import boundary.
@ -2082,7 +2090,7 @@ bitflags! {
/// ```
const COMPREHENSION_ASSIGNMENT = 1 << 20;
/// The model is in a module / class / function docstring.
/// The model is in a docstring as described in [PEP 257].
///
/// For example, the model could be visiting either the module, class,
/// or function docstring in:
@ -2099,7 +2107,9 @@ bitflags! {
/// """Function docstring."""
/// pass
/// ```
const DOCSTRING = 1 << 21;
///
/// [PEP 257]: https://peps.python.org/pep-0257/#what-is-a-docstring
const PEP_257_DOCSTRING = 1 << 21;
/// The model is visiting the r.h.s. of a module-level `__all__` definition.
///
@ -2136,6 +2146,31 @@ bitflags! {
/// while traversing the AST. (This only happens in stub files.)
const DEFERRED_CLASS_BASE = 1 << 25;
/// The model is in an attribute docstring.
///
/// An attribute docstring is a string literal immediately following an assignment or an
/// annotated assignment statement. The context in which this is valid are:
/// 1. At the top level of a module
/// 2. At the top level of a class definition i.e., a class attribute
///
/// For example:
/// ```python
/// a = 1
/// """This is an attribute docstring for `a` variable"""
///
///
/// class Foo:
/// b = 1
/// """This is an attribute docstring for `Foo.b` class variable"""
/// ```
///
/// Unlike other kinds of docstrings as described in [PEP 257], attribute docstrings are
/// discarded at runtime. However, they are used by some documentation renderers and
/// static-analysis tools.
///
/// [PEP 257]: https://peps.python.org/pep-0257/#what-is-a-docstring
const ATTRIBUTE_DOCSTRING = 1 << 26;
/// The context is in any type annotation.
const ANNOTATION = Self::TYPING_ONLY_ANNOTATION.bits() | Self::RUNTIME_EVALUATED_ANNOTATION.bits() | Self::RUNTIME_REQUIRED_ANNOTATION.bits();