Skip to content

Commit 3fe7f97

Browse files
fix: emit missing symbol information (#226)
* fix: emit missing symbol information * test: resolve temporary fixture paths on macOS
1 parent 6306404 commit 3fe7f97

9 files changed

Lines changed: 317 additions & 11 deletions

File tree

‎.github/workflows/scip-snapshot.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ jobs:
4242
fi
4343
- run: npm install
4444
- run: cd ./packages/pyright-scip/ && npm install && npm run build
45+
- run: cd ./packages/pyright-scip/ && npm test -- --runInBand
4546
- run: python --version
4647
- run: cd ./packages/pyright-scip/ && npm run check-snapshots
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from __future__ import annotations
2+
3+
from contracts import Foo
4+
5+
6+
def make_foo(x: float) -> Foo:
7+
return Foo(x=x)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# format-options: showDocs,showRanges
2+
3+
from __future__ import annotations
4+
5+
from dataclasses import dataclass
6+
from numbers import Real
7+
import math
8+
9+
10+
def _validate(value: object) -> None:
11+
if not isinstance(value, Foo):
12+
raise ValueError("must be Foo")
13+
if not isinstance(value.x, Real) or not math.isfinite(value.x):
14+
raise ValueError("x must be finite real")
15+
16+
17+
@dataclass(frozen=True)
18+
class Foo:
19+
x: float
20+
21+
def __post_init__(self) -> None:
22+
_validate(self)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[project]
2+
name = "scip-final-repro"
3+
version = "0.1.0"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# < definition scip-python python scip-final-repro 0.1.0 consumer/__init__:
2+
3+
from __future__ import annotations
4+
# ^^^^^^^^^^ reference python-stdlib 3.11 __future__/__init__:
5+
# ^^^^^^^^^^^ reference python-stdlib 3.11 __future__/annotations.annotations.
6+
7+
from contracts import Foo
8+
# ^^^^^^^^^ reference scip-final-repro 0.1.0 contracts/__init__:
9+
# ^^^ reference scip-final-repro 0.1.0 contracts/Foo#
10+
11+
12+
def make_foo(x: float) -> Foo:
13+
# ^^^^^^^^ definition scip-final-repro 0.1.0 consumer/make_foo().
14+
# ^ definition scip-final-repro 0.1.0 consumer/make_foo().(x)
15+
# ^^^^^ reference python-stdlib 3.11 builtins/float#
16+
# ^^^ reference scip-final-repro 0.1.0 contracts/Foo#
17+
return Foo(x=x)
18+
# ^^^ reference scip-final-repro 0.1.0 contracts/Foo#
19+
# ^ reference scip-final-repro 0.1.0 contracts/Foo#x.
20+
# ^ reference scip-final-repro 0.1.0 consumer/make_foo().(x)
21+
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# < definition scip-python python scip-final-repro 0.1.0 contracts/__init__:
2+
#documentation (module) contracts
3+
4+
# format-options: showDocs,showRanges
5+
6+
from __future__ import annotations
7+
# ^^^^^^^^^^ reference python-stdlib 3.11 __future__/__init__:
8+
# external documentation ```python
9+
# > (module) __future__
10+
# > ```
11+
# external documentation ---
12+
# >
13+
# external documentation Record of phased-in incompatible languag...
14+
# >
15+
# > Each line is of the form:
16+
# >
17+
# > &nbsp;&nbsp;&nbsp;&nbsp;FeatureName = "\...
18+
# > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs...
19+
# >
20+
# > where, normally, OptionalRelease &lt; Ma...
21+
# > of the same form as sys.version\_info:
22+
# >
23+
# > &nbsp;&nbsp;&nbsp;&nbsp;(PY\_MAJOR\_VERS...
24+
# > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PY\_MINOR\...
25+
# > PY\_MICRO\_VERSION, # the 0; an int
26+
# > PY\_RELEASE\_LEVEL, # "alpha", "beta", "...
27+
# > PY\_RELEASE\_SERIAL # the 3; an int
28+
# > &nbsp;&nbsp;&nbsp;&nbsp;)
29+
# >
30+
# > OptionalRelease records the first releas...
31+
# >
32+
# > &nbsp;&nbsp;&nbsp;&nbsp;from \_\_future\...
33+
# >
34+
# > was accepted.
35+
# >
36+
# > In the case of MandatoryReleases that ha...
37+
# > MandatoryRelease predicts the release in...
38+
# > of the language.
39+
# >
40+
# > Else MandatoryRelease records when the f...
41+
# > in releases at or after that, modules no...
42+
# >
43+
# > &nbsp;&nbsp;&nbsp;&nbsp;from \_\_future\...
44+
# >
45+
# > to use the feature in question, but may ...
46+
# >
47+
# > MandatoryRelease may also be None, meani...
48+
# > dropped.
49+
# >
50+
# > Instances of class \_Feature have two co...
51+
# > .getOptionalRelease() and .getMandatoryR...
52+
# >
53+
# > CompilerFlag is the (bitfield) flag that...
54+
# > argument to the builtin function compile...
55+
# > dynamically compiled code. This flag is...
56+
# > attribute on \_Future instances. These ...
57+
# > #defines of CO\_xxx flags in Include/cpy...
58+
# >
59+
# > No feature line is ever to be deleted fr...
60+
# ^^^^^^^^^^^ reference python-stdlib 3.11 __future__/annotations.annotations.
61+
# external documentation ```python
62+
# > (variable) annotations: _Feature
63+
# > ```
64+
65+
from dataclasses import dataclass
66+
# ^^^^^^^^^^^ reference python-stdlib 3.11 dataclasses/__init__:
67+
# external documentation ```python
68+
# > (module) dataclasses
69+
# > ```
70+
# ^^^^^^^^^ reference python-stdlib 3.11 dataclasses/dataclass().
71+
from numbers import Real
72+
# ^^^^^^^ reference python-stdlib 3.11 numbers/__init__:
73+
# external documentation ```python
74+
# > (module) numbers
75+
# > ```
76+
# external documentation ---
77+
# >
78+
# external documentation Abstract Base Classes (ABCs) for numbers...
79+
# >
80+
# > TODO: Fill out more detailed documentati...
81+
# ^^^^ reference python-stdlib 3.11 numbers/Real#
82+
import math
83+
# ^^^^ reference python-stdlib 3.11 math/__init__:
84+
85+
86+
# < start enclosing_range scip-python python scip-final-repro 0.1.0 contracts/_validate().
87+
def _validate(value: object) -> None:
88+
# ^^^^^^^^^ definition scip-final-repro 0.1.0 contracts/_validate().
89+
# documentation ```python
90+
# > def _validate(
91+
# > value: object
92+
# > ) -> None:
93+
# > ```
94+
# ^^^^^ definition scip-final-repro 0.1.0 contracts/_validate().(value)
95+
# ^^^^^^ reference python-stdlib 3.11 builtins/object#
96+
# external documentation ```python
97+
# > (class) object
98+
# > ```
99+
if not isinstance(value, Foo):
100+
# ^^^^^^^^^^ reference local 0
101+
# external documentation ```python
102+
# > (function) def isinstance(
103+
# > __obj: object,
104+
# > __class_or_tuple: _ClassInfo,
105+
# > /
106+
# > ) -> bool
107+
# > ```
108+
# ^^^^^ reference scip-final-repro 0.1.0 contracts/_validate().(value)
109+
# ^^^ reference scip-final-repro 0.1.0 contracts/Foo#
110+
raise ValueError("must be Foo")
111+
# ^^^^^^^^^^ reference python-stdlib 3.11 builtins/ValueError#
112+
# external documentation ```python
113+
# > class ValueError(*args: object)
114+
# > ```
115+
if not isinstance(value.x, Real) or not math.isfinite(value.x):
116+
# ^^^^^^^^^^ reference local 1
117+
# external documentation ```python
118+
# > (function) def isinstance(
119+
# > __obj: object,
120+
# > __class_or_tuple: _ClassInfo,
121+
# > /
122+
# > ) -> bool
123+
# > ```
124+
# ^^^^^ reference scip-final-repro 0.1.0 contracts/_validate().(value)
125+
# ^ reference scip-final-repro 0.1.0 contracts/Foo#x.
126+
# ^^^^ reference python-stdlib 3.11 numbers/Real#
127+
# ^^^^ reference python-stdlib 3.11 math/__init__:
128+
# ^^^^^^^^ reference python-stdlib 3.11 math/isfinite().
129+
# ^^^^^ reference scip-final-repro 0.1.0 contracts/_validate().(value)
130+
# ^ reference scip-final-repro 0.1.0 contracts/Foo#x.
131+
raise ValueError("x must be finite real")
132+
# ^^^^^^^^^^ reference python-stdlib 3.11 builtins/ValueError#
133+
# < end enclosing_range scip-python python scip-final-repro 0.1.0 contracts/_validate().
134+
135+
136+
# < start enclosing_range scip-python python scip-final-repro 0.1.0 contracts/Foo#
137+
@dataclass(frozen=True)
138+
#^^^^^^^^^ reference python-stdlib 3.11 dataclasses/dataclass().
139+
# ^^^^^^ reference python-stdlib 3.11 dataclasses/dataclass().(frozen)
140+
# external documentation ```python
141+
# > (parameter) frozen: bool
142+
# > ```
143+
class Foo:
144+
# ^^^ definition scip-final-repro 0.1.0 contracts/Foo#
145+
# documentation ```python
146+
# > @dataclass(frozen=True)
147+
# > class Foo:
148+
# > ```
149+
x: float
150+
# ^ definition scip-final-repro 0.1.0 contracts/Foo#x.
151+
# documentation ```python
152+
# > (variable) x: float
153+
# > ```
154+
# ^^^^^ reference python-stdlib 3.11 builtins/float#
155+
# external documentation ```python
156+
# > (class) float
157+
# > ```
158+
159+
# ⌄ start enclosing_range scip-python python scip-final-repro 0.1.0 contracts/Foo#__post_init__().
160+
def __post_init__(self) -> None:
161+
# ^^^^^^^^^^^^^ definition scip-final-repro 0.1.0 contracts/Foo#__post_init__().
162+
# documentation ```python
163+
# > def __post_init__(
164+
# > self
165+
# > ) -> None:
166+
# > ```
167+
# ^^^^ definition scip-final-repro 0.1.0 contracts/Foo#__post_init__().(self)
168+
_validate(self)
169+
# ^^^^^^^^^ reference scip-final-repro 0.1.0 contracts/_validate().
170+
# ^^^^ reference scip-final-repro 0.1.0 contracts/Foo#__post_init__().(self)
171+
# ^ end enclosing_range scip-python python scip-final-repro 0.1.0 contracts/Foo#__post_init__().
172+
# < end enclosing_range scip-python python scip-final-repro 0.1.0 contracts/Foo#
173+

‎packages/pyright-scip/src/test-runner.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export interface TestFailure {
1111
| 'content-mismatch'
1212
| 'orphaned-output'
1313
| 'caught-exception'
14-
| 'sometimes-assertion';
14+
| 'sometimes-assertion'
15+
| 'missing-symbol-information';
1516
message: string;
1617
}
1718

‎packages/pyright-scip/src/treeVisitor.ts‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ export class TreeVisitor extends ParseTreeWalker {
508508
if (pythonPackage) {
509509
const symbol = Symbols.makeModuleInit(pythonPackage, moduleName);
510510
this.pushNewOccurrence(node.module, symbol);
511+
this.emitExternalSymbolInformation(node.module, symbol, [], false);
511512
} else {
512513
// For python packages & modules that we cannot resolve,
513514
// we'll just make a local for the file and note that we could not resolve this module.
@@ -566,8 +567,9 @@ export class TreeVisitor extends ParseTreeWalker {
566567
return this.emitDeclarationWithoutNode(node, decl);
567568
}
568569

570+
const isDefinition = decl.node.id === parent.id;
569571
const existingSymbol = this.rawGetLsifSymbol(decl.node);
570-
if (existingSymbol) {
572+
if (existingSymbol && !(isDefinition && decl.node.nodeType === ParseNodeType.Class)) {
571573
if (decl.node.id === parent.id || decl.node.id === node.id) {
572574
switch (decl.node.nodeType) {
573575
case ParseNodeType.Function:
@@ -583,8 +585,6 @@ export class TreeVisitor extends ParseTreeWalker {
583585
return true;
584586
}
585587

586-
const isDefinition = decl.node.id === parent.id;
587-
588588
const builtinType = this.evaluator.getBuiltInType(node, node.value);
589589
if (this.isStdlib(decl, builtinType)) {
590590
this.emitBuiltinScipSymbol(node, builtinType, decl);
@@ -1431,12 +1431,18 @@ export class TreeVisitor extends ParseTreeWalker {
14311431
return this.config.pythonEnvironment.getPackageForModule(moduleName);
14321432
}
14331433

1434-
private emitExternalSymbolInformation(node: ParseNode, symbol: ScipSymbol, documentation: string[]) {
1435-
if (this.externalSymbols.has(symbol.value)) {
1434+
private emitExternalSymbolInformation(
1435+
node: ParseNode,
1436+
symbol: ScipSymbol,
1437+
documentation: string[],
1438+
lookupDocumentation = true
1439+
) {
1440+
const existing = this.externalSymbols.get(symbol.value);
1441+
if (existing && existing.documentation.length > 0) {
14361442
return;
14371443
}
14381444

1439-
if (documentation.length === 0) {
1445+
if (documentation.length === 0 && lookupDocumentation) {
14401446
const nodeFileInfo = getFileInfo(node)!;
14411447
const hoverResult = this.program.getHoverForPosition(
14421448
nodeFileInfo.filePath,
@@ -1450,10 +1456,6 @@ export class TreeVisitor extends ParseTreeWalker {
14501456
}
14511457
}
14521458

1453-
if (documentation.length === 0) {
1454-
return;
1455-
}
1456-
14571459
// TODO: Could consider adding the documentation finder stuff
14581460
// from emitSymbolInformationOnce, but at this point we don't
14591461
// need that.

0 commit comments

Comments
 (0)