diff --git a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts index f554ebbfd..34707b7dd 100644 --- a/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts +++ b/packages/pyright-internal/src/analyzer/semanticTokensWalker.ts @@ -1,6 +1,16 @@ import { ParseTreeWalker } from './parseTreeWalker'; import { TypeEvaluator } from './typeEvaluatorTypes'; -import { ClassType, FunctionType, getTypeAliasInfo, OverloadedType, Type, TypeCategory, TypeFlags } from './types'; +import { + ClassType, + ClassTypeFlags, + FunctionType, + getTypeAliasInfo, + isClass, + OverloadedType, + Type, + TypeCategory, + TypeFlags, +} from './types'; import { ClassNode, DecoratorNode, @@ -8,6 +18,7 @@ import { ImportAsNode, ImportFromAsNode, ImportFromNode, + isExpressionNode, LambdaNode, NameNode, ParameterNode, @@ -181,7 +192,23 @@ export class SemanticTokensWalker extends ParseTreeWalker { break; case TypeCategory.Class: //type annotations handled by visitTypeAnnotation - if (!(type.flags & TypeFlags.Instance)) { + 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 { // 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 735600f1c..1b6556cbb 100644 --- a/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py +++ b/packages/pyright-internal/src/tests/samples/semantic_highlighting/final.py @@ -3,4 +3,17 @@ FOO = 1 foo: Final = 2 _ = 3 -__: Final = 4 \ No newline at end of file +__: Final = 4 + +class Foo: + @property + def foo(self) -> int: ... + + @property + def bar(self) -> int: ... + @foo.setter + def bar(self, value: int): ... + + +Foo().foo +Foo().bar \ 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 1ca5b228c..77a99f9d2 100644 --- a/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts +++ b/packages/pyright-internal/src/tests/semanticTokensProvider.test.ts @@ -45,6 +45,35 @@ if (process.platform !== 'win32' || !process.env['CI']) { { type: 'variable', modifiers: [], start: 49, length: 1 }, { type: 'variable', modifiers: ['readonly'], start: 55, length: 2 }, { type: 'class', modifiers: [], start: 59, length: 5 }, + { type: 'class', modifiers: ['definition'], start: 76, length: 3 }, + { type: 'class', modifiers: [], start: 76, length: 3 }, + { type: 'method', modifiers: ['definition'], start: 103, length: 3 }, + { type: 'decorator', modifiers: [], start: 85, length: 1 }, + { type: 'decorator', modifiers: [], start: 86, length: 8 }, + { type: 'class', modifiers: ['defaultLibrary', 'builtin'], start: 86, length: 8 }, + { type: 'method', modifiers: [], start: 103, length: 3 }, + { type: 'parameter', modifiers: ['definition'], start: 107, length: 4 }, + { type: 'class', modifiers: ['defaultLibrary', 'builtin'], start: 116, length: 3 }, + { type: 'method', modifiers: ['definition'], start: 148, length: 3 }, + { type: 'decorator', modifiers: [], start: 130, length: 1 }, + { type: 'decorator', modifiers: [], start: 131, length: 8 }, + { type: 'class', modifiers: ['defaultLibrary', 'builtin'], start: 131, length: 8 }, + { type: 'method', modifiers: [], start: 148, length: 3 }, + { type: 'parameter', modifiers: ['definition'], start: 152, length: 4 }, + { type: 'class', modifiers: ['defaultLibrary', 'builtin'], start: 161, length: 3 }, + { type: 'method', modifiers: ['definition'], start: 194, length: 3 }, + { type: 'decorator', modifiers: [], start: 174, length: 1 }, + { type: 'decorator', modifiers: [], start: 179, length: 6 }, + { type: 'variable', modifiers: [], start: 175, length: 3 }, + { type: 'function', modifiers: [], start: 179, length: 6 }, + { type: 'method', modifiers: [], start: 194, length: 3 }, + { 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: '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 }, ]); });