Skip to content

Fix implicit concatenation of t-string literals with str or bytes - #11635

Open
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/tstring-str-implicit-concatenation
Open

Fix implicit concatenation of t-string literals with str or bytes#11635
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/tstring-str-implicit-concatenation

Conversation

@hsusul

Copy link
Copy Markdown
Contributor

Summary

  • CPython 3.14 and PEP 750 reject implicit concatenation of a t-string literal with a str, bytes, or f-string literal (SyntaxError: cannot mix t-string literals with string or bytes literals).
  • Pyright previously accepted mixes such as t"x" "y" and "" t"x", and inferred Template. Adjacent t-string literals (t"a" t"b") remain valid.
  • The check is implemented next to the existing bytes/str implicit-concatenation diagnostic, so mixed t-string concatenations are reported and typed as Unknown.

Reproduction (Python 3.14)

t1 = t"Hello " t"World"  # valid Template
t2 = t"Hello " "World"   # SyntaxError at runtime; Pyright now reports an error
t3 = t"x" + t"y"         # valid
t4 = t"x" + "y"          # TypeError at runtime; already reported via Template.__add__

CPython:

>>> t"a" "b"
SyntaxError: cannot mix t-string literals with string or bytes literals

Root cause

getTypeOfStringList treated any string list containing a t-string as Template, including mixed implicit concatenations. That matched an earlier draft of PEP 750; the final spec and CPython 3.14 disallow Template/str (and Template/bytes) implicit concatenation.

Test plan

  • tstring2.py: valid t-string implicit concat; mix with str, f-string, and bytes; explicit Template + Template vs Template + str
  • npx jest typeEvaluator4.test.ts -t TString --forceExit (pass)
  • npx jest typeEvaluator4.test.ts --forceExit (155 passed)
  • npx jest localizer.test.ts --forceExit (pass)
  • npx jest typeEvaluator8.test.ts -t Strings2 --forceExit from packages/pyright-internal (pass)
  • ESLINT_USE_FLAT_CONFIG=false npx eslint on changed TS files (pass)
  • npx prettier -c on changed TS/JSON files (pass)
  • npx lerna exec --stream --no-bail --ignore=pyright -- "tsc --noEmit" (pass)
  • git diff --check (pass)

Full packages/pyright-internal npm test was not run (includes webpack test server + entire Jest suite). Analyzer coverage for this change is the TString / string-concatenation sample tests.

PEP 750 and CPython 3.14 reject mixing t-string literals with string or bytes literals. Report that mix instead of inferring Template.
@rchiodo

Rich Chiodo (rchiodo) commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

t5 = "" t"x"

# This should generate an error.
t6 = t"x" "y"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Warning · Non-blocking recommendation

The tests only contribute to an aggregate diagnostic count and do not verify the new Unknown recovery type. Add reveal_type coverage for at least one mixed expression so a regression to Template inference cannot pass.

[verified]

@rchiodo

Copy link
Copy Markdown
Collaborator

Non-blocking follow-up: mirror this validation in Pylance's getTypeOfStringList_ForStubLoading counterpart when the Pyright change is vendored.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) added the review-auto:approved Automated review: no blocking findings (approval posted). label Aug 13, 2026
configOptions.defaultPythonVersion = pythonVersion3_14;
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['tstring2.py'], configOptions);
TestUtils.validateResults(analysisResults1, 1);
TestUtils.validateResults(analysisResults1, 6);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Warning · Non-blocking recommendation

The aggregate count does not verify that mixed literals receive the new diagnostic and are typed as Unknown; another diagnostic could preserve the count while this behavior regresses. Add reveal_type(..., expected_text="Unknown") coverage for a representative invalid form and a diagnostic-specific assertion if supported by the harness.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved via Review Center.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved via Review Center.

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

Labels

review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants