@zzzen/pyright-internal 1.2.0-dev.20260628 → 1.2.0-dev.20260705

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -291,12 +291,35 @@ class Binder extends parseTreeWalker_1.ParseTreeWalker {
291
291
  AnalyzerNodeInfo.setScope(node, this._currentScope);
292
292
  this._addImplicitSymbolToCurrentScope('__doc__', node, 'str | None');
293
293
  this._addImplicitSymbolToCurrentScope('__module__', node, 'str');
294
- this._addImplicitSymbolToCurrentScope('__qualname__', node, 'str');
295
294
  this._dunderSlotsEntries = undefined;
296
295
  if (!this._moduleSymbolOnly) {
297
296
  // Analyze the suite.
298
297
  this.walk(node.d.suite);
299
298
  }
299
+ // `__qualname__` is exposed via the metaclass (`type`) rather than as a
300
+ // class/instance attribute, unlike `__doc__`/`__module__`. We handle it
301
+ // after walking the suite so we can tell whether the class body already
302
+ // declared it.
303
+ const existingQualname = this._currentScope.lookUpSymbol('__qualname__');
304
+ if (existingQualname) {
305
+ // The class explicitly declares `__qualname__` (e.g. typeshed `type`,
306
+ // `function`, or a user `__qualname__ = "..."`). Keep that real
307
+ // declaration untouched so hover, go-to-definition, and completion all
308
+ // resolve to it. We must not append a synthetic empty-range Intrinsic
309
+ // declaration on top, because declaration-selecting consumers (e.g.
310
+ // `getLastTypedDeclarationForSymbol`) would otherwise resolve to the
311
+ // empty range instead of the real declaration. We still mark it as
312
+ // ignored for protocol matching, matching the implicit-dunder treatment.
313
+ existingQualname.setIsIgnoredForProtocolMatch();
314
+ }
315
+ else {
316
+ // The class does not declare `__qualname__`. Add it as a non-class
317
+ // member so it is name-resolvable within the class body (e.g.
318
+ // `print(__qualname__)`) but is not exposed as a class/instance
319
+ // attribute. Otherwise instance access (`instance.__qualname__`) would
320
+ // incorrectly resolve instead of reporting an attribute-access error.
321
+ this._addImplicitSymbolToCurrentScope('__qualname__', node, 'str', /* isClassMember */ false);
322
+ }
300
323
  if (this._dunderSlotsEntries) {
301
324
  this._addSlotsToCurrentScope(this._dunderSlotsEntries);
302
325
  }
@@ -2891,8 +2914,8 @@ class Binder extends parseTreeWalker_1.ParseTreeWalker {
2891
2914
  }
2892
2915
  }
2893
2916
  }
2894
- _addImplicitSymbolToCurrentScope(nameValue, node, type) {
2895
- const symbol = this._addSymbolToCurrentScope(nameValue, /* isInitiallyUnbound */ false);
2917
+ _addImplicitSymbolToCurrentScope(nameValue, node, type, isClassMember = true) {
2918
+ const symbol = this._addSymbolToCurrentScope(nameValue, /* isInitiallyUnbound */ false, isClassMember);
2896
2919
  if (symbol) {
2897
2920
  symbol.addDeclaration({
2898
2921
  type: 0 /* DeclarationType.Intrinsic */,
@@ -2908,14 +2931,14 @@ class Binder extends parseTreeWalker_1.ParseTreeWalker {
2908
2931
  }
2909
2932
  }
2910
2933
  // Adds a new symbol with the specified name if it doesn't already exist.
2911
- _addSymbolToCurrentScope(nameValue, isInitiallyUnbound) {
2934
+ _addSymbolToCurrentScope(nameValue, isInitiallyUnbound, isClassMember = true) {
2912
2935
  let symbol = this._currentScope.lookUpSymbol(nameValue);
2913
2936
  if (!symbol) {
2914
2937
  let symbolFlags = 0 /* SymbolFlags.None */;
2915
2938
  if (isInitiallyUnbound) {
2916
2939
  symbolFlags |= 1 /* SymbolFlags.InitiallyUnbound */;
2917
2940
  }
2918
- if (this._currentScope.type === 3 /* ScopeType.Class */) {
2941
+ if (this._currentScope.type === 3 /* ScopeType.Class */ && isClassMember) {
2919
2942
  symbolFlags |= 4 /* SymbolFlags.ClassMember */;
2920
2943
  }
2921
2944
  if (this._fileInfo.isStubFile && (0, symbolNameUtils_1.isPrivateOrProtectedName)(nameValue)) {
@@ -3428,6 +3451,29 @@ class Binder extends parseTreeWalker_1.ParseTreeWalker {
3428
3451
  if (!importedModuleAliasDecl) {
3429
3452
  return false;
3430
3453
  }
3454
+ // The imported symbol may be both an implicitly-imported submodule and a
3455
+ // class/function/variable of the same name (e.g. a package that re-exports
3456
+ // a class whose name matches a submodule). In that case the non-module
3457
+ // declaration appears later in the declaration list and "wins" when the
3458
+ // symbol is resolved. Only treat this wildcard re-export as a pure submodule
3459
+ // re-export when the module alias is the symbol's last declaration;
3460
+ // otherwise fall through so a normal alias declaration is created that
3461
+ // resolves to the winning symbol.
3462
+ //
3463
+ // We compare against the raw last declaration (not getLastTypedDeclarationForSymbol)
3464
+ // on purpose: a module alias is a DeclarationType.Alias, which hasTypeForDeclaration
3465
+ // treats as untyped, so it never appears among a symbol's typed declarations.
3466
+ // The evaluator resolves alias symbols (like this one, whose declarations are all
3467
+ // imports) by declaration order, so the last declaration is the relevant "winner"
3468
+ // here. When this guard falls through, the alias created by the caller resolves to
3469
+ // the winning (e.g. class) declaration and intentionally has no submoduleFallback:
3470
+ // the class shadows the submodule, so submodule member access through the
3471
+ // re-exported name is no longer offered. The genuine-submodule case (where the
3472
+ // module alias is the last declaration) keeps its module/submodule behavior.
3473
+ const importedDecls = importedSymbol.getDeclarations();
3474
+ if (importedDecls[importedDecls.length - 1] !== importedModuleAliasDecl) {
3475
+ return false;
3476
+ }
3431
3477
  const existingModuleAliasDecl = this._getMultipartModuleAliasDeclaration(localSymbol, importedModuleAliasDecl.moduleName, importedModuleAliasDecl.firstNamePart);
3432
3478
  if (existingModuleAliasDecl) {
3433
3479
  this._mergeModuleLoaderActions(existingModuleAliasDecl, importedModuleAliasDecl);