diff --git a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts index 34707b7dd..a86d8a1c3 100644 --- a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts +++ b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts @@ -18,8 +18,8 @@ import { ImportAsNode, ImportFromAsNode, ImportFromNode, - isExpressionNode, LambdaNode, + MemberAccessNode, NameNode, ParameterNode, ParseNodeType, @@ -135,6 +135,21 @@ export class SemanticTokensWalker extends ParseTreeWalker { return super.visitTypeAlias(node); } + override visitMemberAccess(node: MemberAccessNode): boolean { + // check for properties without a setter + if (node.parent && this._evaluator) { + const declaredType = this._evaluator.getDeclaredTypeForExpression(node, { + method: 'set', + }); + if (declaredType && isClass(declaredType) && declaredType.shared.flags & ClassTypeFlags.PropertyClass) { + this._addItem(node.d.member.start, node.d.member.length, SemanticTokenTypes.variable, [ + SemanticTokenModifiers.readonly, + ]); + } + } + return super.visitMemberAccess(node); + } + private _visitNameWithType(node: NameNode, type: Type | undefined) { switch (type?.category) { case TypeCategory.Function: @@ -192,23 +207,7 @@ export class SemanticTokensWalker extends ParseTreeWalker { break; case TypeCategory.Class: //type annotations handled by visitTypeAnnotation - if (type.flags & TypeFlags.Instance) { - if (node.parent && this._evaluator && isExpressionNode(node.parent)) { - const declaredType = this._evaluator.getDeclaredTypeForExpression(node.parent, { - method: 'set', - }); - if ( - declaredType && - isClass(declaredType) && - declaredType.shared.flags & ClassTypeFlags.PropertyClass - ) { - this._addItem(node.start, node.length, SemanticTokenTypes.variable, [ - SemanticTokenModifiers.readonly, - ]); - return; - } - } - } else { + if (!(type.flags & TypeFlags.Instance)) { // Exclude type aliases: // PEP 613 > Name: TypeAlias = Types // PEP 695 > type Name = Types diff --git a/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py b/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py index 1b6556cbb..c0dd052fa 100644 --- a/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py +++ b/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py @@ -16,4 +16,7 @@ def bar(self, value: int): ... Foo().foo -Foo().bar \ No newline at end of file +Foo().bar + +baz = Foo() +_ = baz.foo \ No newline at end of file diff --git a/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts b/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts index 77a99f9d2..ce7ff5df0 100644 --- a/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts +++ b/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts @@ -70,10 +70,17 @@ if (process.platform !== 'win32' || !process.env['CI']) { { type: 'parameter', modifiers: ['definition'], start: 198, length: 4 }, { type: 'parameter', modifiers: ['definition'], start: 204, length: 5 }, { type: 'class', modifiers: ['defaultLibrary', 'builtin'], start: 211, length: 3 }, + { type: 'variable', modifiers: ['readonly'], start: 229, length: 3 }, { type: 'class', modifiers: [], start: 223, length: 3 }, { type: 'variable', modifiers: ['readonly'], start: 229, length: 3 }, { type: 'class', modifiers: [], start: 233, length: 3 }, { type: 'variable', modifiers: [], start: 239, length: 3 }, + { type: 'variable', modifiers: [], start: 244, length: 3 }, + { type: 'class', modifiers: [], start: 250, length: 3 }, + { type: 'variable', modifiers: [], start: 256, length: 1 }, + { type: 'variable', modifiers: ['readonly'], start: 264, length: 3 }, + { type: 'variable', modifiers: [], start: 260, length: 3 }, + { type: 'variable', modifiers: ['readonly'], start: 264, length: 3 }, ]); });