Skip to content

Commit 7df2cd6

Browse files
knQzxmeta-codesync[bot]
authored andcommitted
flag ambiguous protocol members without type annotations (#3012)
Summary: - add new `UnannotatedProtocolMember` error kind for protocol members assigned a value without an explicit type annotation - matches mypy behavior - protocol members must have declared types - updates existing bug test case to verify the new diagnostic fires correctly Pull Request resolved: #3012 Test Plan: - updated `test_protocol_ambiguous_member` to expect the new error on unannotated assignments like `x = None` - added positive case (`class Ok(Protocol)`) with annotated members to confirm no false positives - all 34 existing protocol tests still pass fixes #2925 Reviewed By: rchen152 Differential Revision: D100014044 Pulled By: stroxler fbshipit-source-id: 41fe7b177036511fdbf708a6cf9b84077b03d111
1 parent 305dfc9 commit 7df2cd6

File tree

4 files changed

+37
-5
lines changed

4 files changed

+37
-5
lines changed

crates/pyrefly_config/src/error_kind.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ pub enum ErrorKind {
259259
UnannotatedAttribute,
260260
/// A function parameter is missing a type annotation.
261261
UnannotatedParameter,
262+
/// A protocol member is assigned a value in the class body without an explicit type annotation.
263+
UnannotatedProtocolMember,
262264
/// A function is missing a return type annotation.
263265
UnannotatedReturn,
264266
/// Attempting to use a name that may be unbound or uninitialized

pyrefly/lib/alt/class/class_field.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,20 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
15881588
..
15891589
} => {
15901590
let direct_annotation = annot.map(|a| self.get_idx(a).annotation.clone());
1591+
if metadata.is_protocol()
1592+
&& direct_annotation.is_none()
1593+
&& !is_dunder(name.as_str())
1594+
{
1595+
self.error(
1596+
errors,
1597+
range,
1598+
ErrorInfo::Kind(ErrorKind::UnannotatedProtocolMember),
1599+
format!(
1600+
"Protocol member `{}` must have an explicit type annotation",
1601+
name,
1602+
),
1603+
);
1604+
}
15911605
let initialization = if let ExprOrBinding::Expr(e) = value.as_ref()
15921606
&& let Some(dm) = metadata.dataclass_metadata()
15931607
&& let Expr::Call(call) = e

pyrefly/lib/test/protocol.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,15 +1008,16 @@ def to_foo() -> Foo[MySeries]:
10081008

10091009
// https://github.com/facebook/pyrefly/issues/2925
10101010
testcase!(
1011-
bug = "Should detect ambiguous protocol members with value assignments",
10121011
test_protocol_ambiguous_member,
10131012
r#"
10141013
from typing import Protocol
10151014
10161015
class Ambiguous(Protocol):
1017-
# Assigning a value in a Protocol body is ambiguous: is it declaring
1018-
# a member with a type, or providing a default value?
1019-
x = None
1020-
y = ...
1016+
x = None # E: Protocol member `x` must have an explicit type annotation
1017+
y = ... # E: Protocol member `y` must have an explicit type annotation
1018+
1019+
class Ok(Protocol):
1020+
x: int
1021+
y: str = "default"
10211022
"#,
10221023
);

website/docs/error-kinds.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,21 @@ def process_data(x: int, y: int) -> int:
12641264
return x + y
12651265
```
12661266

1267+
## unannotated-protocol-member
1268+
1269+
This error is raised when a protocol member is assigned a value in the class body without an explicit type annotation. Protocol members must have explicitly declared types so that implementations know exactly what type to provide.
1270+
1271+
```python
1272+
from typing import Protocol
1273+
1274+
class MyProto(Protocol):
1275+
x = None # error: Protocol member `x` must have an explicit type annotation
1276+
1277+
# Fixed version:
1278+
class MyProto(Protocol):
1279+
x: int | None = None
1280+
```
1281+
12671282
## unannotated-return
12681283

12691284
This error is raised when a function is missing a return type annotation. This helps enforce fully-typed codebases by ensuring all functions declare their return types explicitly. To fix it, add a return type annotation to the function.

0 commit comments

Comments
 (0)