Skip to content

fix [feature] warn when passing str to something that accepts iterable[str] or sequence[str] #673#2293

Closed
asukaminato0721 wants to merge 9 commits into
facebook:mainfrom
asukaminato0721:673
Closed

fix [feature] warn when passing str to something that accepts iterable[str] or sequence[str] #673#2293
asukaminato0721 wants to merge 9 commits into
facebook:mainfrom
asukaminato0721:673

Conversation

@asukaminato0721

Copy link
Copy Markdown
Collaborator

Summary

Fixes #673

Implemented the new string-as-iterable warning and documented it, with checks that fire only when the argument is definitely str and the parameter is Iterable[str]/Sequence[str] without explicitly allowing str.

Test Plan

add test

@meta-cla meta-cla Bot added the cla signed label Feb 3, 2026
@github-actions

This comment has been minimized.

@asukaminato0721
asukaminato0721 marked this pull request as draft February 3, 2026 09:38
@github-actions

This comment has been minimized.

@asukaminato0721
asukaminato0721 marked this pull request as ready for review February 3, 2026 13:06
@yangdanny97 yangdanny97 self-assigned this Feb 3, 2026

@yangdanny97 yangdanny97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems to change the positions of a bunch of unrelated errors.

I think the change could be smaller and more self-contained.

For now, what if we didn't consider unions or assignability at all, and just made it error if the expected type is exactly Iterable[str] or Sequence[str] and the actual type is exactly str?

For the initial impl we probably don't even need to consider unions.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@meta-codesync

meta-codesync Bot commented Feb 4, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D92301565.

@yangdanny97

Copy link
Copy Markdown
Contributor

This is a lot of new errors, I'll need to think about how to restrict this so we don't give so many false positives. Additionally, it should probably be off by default

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the stale label Feb 23, 2026
@yangdanny97 yangdanny97 reopened this Mar 11, 2026
@github-actions github-actions Bot removed the stale label Mar 16, 2026
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the stale label Mar 31, 2026
@yangdanny97 yangdanny97 removed their assignment Mar 31, 2026
Copilot AI review requested due to automatic review settings April 10, 2026 12:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a new diagnostic to Pyrefly that warns when a str is passed to parameters typed as Iterable[str] or Sequence[str], aiming to catch the common footgun where strings are treated as iterables of characters.

Changes:

  • Introduces ErrorKind::StringAsIterable (default severity warn) and emits it during argument type-checking when the pattern is detected.
  • Documents the new error kind and adds a regression test covering Iterable[str] / Sequence[str] and an explicit | str allowlist case.
  • Refactors overload calling to optionally retry without a hint when hint-driven checking produces call errors.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
website/docs/error-kinds.mdx Adds documentation for the new string-as-iterable diagnostic.
pyrefly/lib/test/calls.rs Adds a test case asserting the new warning message for Iterable[str] and Sequence[str].
pyrefly/lib/alt/overload.rs Updates overload call flow to retry without hint when hint-driven call errors occur.
pyrefly/lib/alt/callable.rs Implements the string-as-iterable warning and threads it through argument checking.
crates/pyrefly_config/src/error_kind.rs Adds StringAsIterable to ErrorKind and assigns default severity warn.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +490 to +513
fn check_expr_argument(
&self,
expr: &Expr,
hint: &Type,
range: TextRange,
arg_errors: &ErrorCollector,
call_errors: &ErrorCollector,
tcc: &dyn Fn() -> TypeCheckContext,
) -> (Type, bool) {
if hint.is_any() {
return (
self.expr_infer_type_info_with_hint(expr, None, arg_errors)
.into_ty(),
false,
);
}
let got = self.expr_infer_type_info_with_hint(
expr,
Some(HintRef::new(hint, Some(call_errors))),
arg_errors,
);
let ok = self.check_type(got.ty(), hint, range, call_errors, tcc);
(got.into_ty(), ok)
}

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

check_expr_argument always returns the inferred type even when check_type fails. Previously expr_with_separate_check_errors would return want on failure (via check_and_return_type_info), which helps prevent cascading type errors and keeps downstream inference consistent. Consider restoring that behavior by returning hint (or got.with_ty(hint.clone())) when ok is false.

Copilot uses AI. Check for mistakes.
Comment on lines +522 to +528
if got.is_error() || got.is_any() || want.is_any() {
return;
}
let got_is_str = matches!(got, Type::ClassType(cls) if cls.is_builtin("str"));
if !got_is_str {
return;
}

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

The new warning only triggers when got is exactly Type::ClassType(str), so common cases like passing a string literal ("hello") or a LiteralString-typed value (both represented as literal-string types) won't warn even though they are definitely str at runtime. Consider treating got.is_literal_string() as str for this check (and add a test for the literal-literal case).

Copilot uses AI. Check for mistakes.
Comment on lines +886 to +895
// We want to use our hint to contextually type the arguments, but errors resulting
// from the hint should not influence overload selection. If there are call errors, we
// try again without a hint in case we can still match this overload.
let (call_errors, res, expected_types) = try_call(hint);
let (call_errors, res, expected_types) =
if tparams.is_some() && hint.is_some() && !call_errors.is_empty() {
try_call(None)
} else {
(call_errors, res, expected_types)
};

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

This overload-calling logic now may run callable_infer twice (first with a hint, then without) to avoid hint-driven errors affecting overload matching. Since this is a behavior/perf change beyond the new string-as-iterable warning, it would be good to mention it in the PR description (or split it) so the diagnostic/selection change is reviewed intentionally.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is this the reason for the new errors o nmatching overloads? is this necessary for this change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, but only for specialization errors, not NoMatchingOverload.

We still retry without the hint when there are call_errors, so hint-driven call-shape failures should not affect overload selection.

The change is that we do not retry away specialization errors anymore.

That is necessary here because the contextual hint from bounded_str(...) is what exposes the TypeVar bound violation; retrying without the hint loses the expected diagnostic.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@kinto0

kinto0 commented May 21, 2026

Copy link
Copy Markdown
Contributor

those new errors don't seem correct, do they?

also, some test failures

@github-actions github-actions Bot added size/l and removed size/l labels May 21, 2026
@github-actions

This comment has been minimized.

@asukaminato0721
asukaminato0721 requested a review from kinto0 May 21, 2026 15:25
@github-actions github-actions Bot added size/l and removed size/l labels May 21, 2026
@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@yangdanny97 yangdanny97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review automatically exported from Phabricator review in Meta.

@meta-codesync

meta-codesync Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

This pull request has been merged in 24e203f.

@asukaminato0721
asukaminato0721 deleted the 673 branch May 27, 2026 02:00
@kinto0

kinto0 commented May 27, 2026

Copy link
Copy Markdown
Contributor

fyi: I redid a little of your PR in this based upon some feedback:

  • moved warn_if_string_as_iterable to answers_solver to make it only called in one callsite
  • changed it to default to None
  • added support for stringliteral

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] warn when passing str to something that accepts iterable[str] or sequence[str]

4 participants