8000 fixed nested call signature help bug (#3667) · codemuse-app/scip-python@f3e85ee · GitHub
[go: up one dir, main page]

Skip to content

Commit f3e85ee

Browse files
authored
fixed nested call signature help bug (#3667)
1 parent 5763cf1 commit f3e85ee

19 files changed

+1864
-1866
lines changed

.github/workflows/validation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
- run: npm run install:all
6666

6767
- name: Check diff after npm install
68-
run: git diff -w --exit-code --name-only
68+
run: git diff --exit-code --name-only
6969

7070
- run: npm run check
7171

package-lock.json

Lines changed: 1559 additions & 1832 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"eslint-plugin-simple-import-sort": "^7.0.0",
3232
"glob": "^7.2.3",
3333
"jsonc-parser": "^3.0.0",
34-
"lerna": "^5.1.4",
34+
"lerna": "^5.1.6",
3535
"npm-check-updates": "^12.5.12",
3636
"p-queue": "^6.6.2",
3737
"prettier": "2.7.1",

packages/pyright-internal/src/analyzer/analyzerFileInfo.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
1313
import { TextRange } from '../common/textRange';
1414
import { TextRangeCollection } from '../common/textRangeCollection';
1515
import { Scope } from './scope';
16+
import { IPythonMode } from './sourceFile';
1617
import { SymbolTable } from './symbol';
1718

1819
// Maps import paths to the symbol table for the imported module.
@@ -47,7 +48,7 @@ export interface AnalyzerFileInfo {
4748
isTypingExtensionsStubFile: boolean;
4849
isBuiltInStubFile: boolean;
4950
isInPyTypedPackage: boolean;
50-
isIPythonMode: boolean;
51+
ipythonMode: IPythonMode;
5152
accessedSymbolSet: Set<number>;
5253
}
5354

packages/pyright-internal/src/analyzer/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,7 @@ export class Binder extends ParseTreeWalker {
15331533
// Make sure this is within an async lambda or function.
15341534
const enclosingFunction = ParseTreeUtils.getEnclosingFunction(node);
15351535
if (enclosingFunction === undefined || !enclosingFunction.isAsync) {
1536-
if (this._fileInfo.isIPythonMode && enclosingFunction === undefined) {
1536+
if (this._fileInfo.ipythonMode && enclosingFunction === undefined) {
15371537
// Top level await is allowed in ipython mode.
15381538
return true;
15391539
}

packages/pyright-internal/src/analyzer/checker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import { ParseTreeWalker } from './parseTreeWalker';
9999
import { validateClassPattern } from './patternMatching';
100100
import { ScopeType } from './scope';
101101
import { getScopeForNode } from './scopeUtils';
102+
import { IPythonMode } from './sourceFile';
102103
import { isStubFile } from './sourceMapper';
103104
import { evaluateStaticBoolExpression } from './staticExpressions';
104105
import { Symbol } from './symbol';
@@ -2782,7 +2783,11 @@ export class Checker extends ParseTreeWalker {
27822783
private _conditionallyReportUnusedSymbol(name: string, symbol: Symbol, scopeType: ScopeType) {
27832784
const accessedSymbolSet = this._fileInfo.accessedSymbolSet;
27842785

2785-
if (symbol.isIgnoredForProtocolMatch() || accessedSymbolSet.has(symbol.id)) {
2786+
if (
2787+
symbol.isIgnoredForProtocolMatch() ||
2788+
accessedSymbolSet.has(symbol.id) ||
2789+
this._fileInfo.ipythonMode === IPythonMode.CellDocs
2790+
) {
27862791
return;
27872792
}
27882793

packages/pyright-internal/src/analyzer/program.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ import { ImportResult, ImportType } from './importResult';
7676
import { findNodeByOffset, getDocString } from './parseTreeUtils';
7777
import { Scope } from './scope';
7878
import { getScopeForNode } from './scopeUtils';
79-
import { SourceFile } from './sourceFile';
79+
import { IPythonMode, SourceFile } from './sourceFile';
8080
import { isUserCode } from './sourceFileInfoUtils';
8181
import { isStubFile, SourceMapper } from './sourceMapper';
8282
import { Symbol } from './symbol';
@@ -156,7 +156,7 @@ export type PreCheckCallback = (parseResults: ParseResults, evaluator: TypeEvalu
156156

157157
export interface OpenFileOptions {
158158
isTracked: boolean;
159-
ipythonMode: boolean;
159+
ipythonMode: IPythonMode;
160160
chainedFilePath: string | undefined;
161161
}
162162

@@ -311,7 +311,7 @@ export class Program {
311311
/* isInPyTypedPackage */ false,
312312
this._console,
313313
this._logTracker,
314-
options?.ipythonMode ?? false
314+
options?.ipythonMode ?? IPythonMode.None
315315
);
316316

317317
const chainedFilePath = options?.chainedFilePath;

packages/pyright-internal/src/analyzer/service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { BackgroundAnalysisProgram, BackgroundAnalysisProgramFactory } from './b
6666
import { createImportedModuleDescriptor, ImportResolver, ImportResolverFactory } from './importResolver';
6767
import { MaxAnalysisTime, Program } from './program';
6868
import { findPythonSearchPaths } from './pythonPathUtils';
69+
import { IPythonMode } from './sourceFile';
6970
import { TypeEvaluator } from './typeEvaluatorTypes';
7071

7172
export const configFileNames = ['pyrightconfig.json'];
@@ -247,7 +248,7 @@ export class AnalyzerService {
247248
path: string,
248249
version: number | null,
249250
contents: string,
250-
ipythonMode = false,
251+
ipythonMode = IPythonMode.None,
251252
chainedFilePath?: string
252253
) {
253254
this._backgroundAnalysisProgram.setFileOpened(path, version, contents, {
@@ -271,7 +272,7 @@ export class AnalyzerService {
271272
path: string,
272273
version: number | null,
273274
contents: TextDocumentContentChangeEvent[],
274-
ipythonMode = false,
275+
ipythonMode = IPythonMode.None,
275276
chainedFilePath?: string
276277
) {
277278
this._backgroundAnalysisProgram.updateOpenFileContents(path, version, contents, {

packages/pyright-internal/src/analyzer/sourceFile.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ interface ResolveImportResult {
7777
ipythonDisplayImportResult?: ImportResult | undefined;
7878
}
7979

80+
// Indicates whether IPython syntax is supported and if so, what
81+
// type of notebook support is in use.
82+
export enum IPythonMode {
83+
// Not a notebook. This is the only falsy enum value, so you
84+
// can test if IPython is supported via "if (ipythonMode)"
85+
None = 0,
86+
// All cells are concatenated into a single document.
87+
ConcatDoc,
88+
// Each cell is its own document.
89+
CellDocs,
90+
}
91+
8092
export class SourceFile {
8193
// Console interface to use for debugging.
8294
private _console: ConsoleInterface;
@@ -175,7 +187,7 @@ export class SourceFile {
175187
private _indexingNeeded = true;
176188

177189
// Indicate whether this file is for ipython or not.
178-
private _ipythonMode = false;
190+
private _ipythonMode = IPythonMode.None;
179191

180192
// Information about implicit and explicit imports from this file.
181193
private _imports: ImportResult[] | undefined;
@@ -193,7 +205,7 @@ export class SourceFile {
193205
isThirdPartyPyTypedPresent: boolean,
194206
console?: ConsoleInterface,
195207
logTracker?: LogTracker,
196-
ipythonMode = false
208+
ipythonMode = IPythonMode.None
197209
) {
198210
this.fileSystem = fs;
199211
this._console = console || new StandardConsole();
@@ -1279,7 +1291,7 @@ export class SourceFile {
12791291
}
12801292

12811293
test_enableIPythonMode(enable: boolean) {
1282-
this._ipythonMode = enable;
1294+
this._ipythonMode = enable ? IPythonMode.ConcatDoc : IPythonMode.None;
12831295
}
12841296

12851297
private _buildFileInfo(
@@ -1309,7 +1321,7 @@ export class SourceFile {
13091321
isTypingExtensionsStubFile: this._isTypingExtensionsStubFile,
13101322
isBuiltInStubFile: this._isBuiltInStubFile,
13111323
isInPyTypedPackage: this._isThirdPartyPyTypedPresent,
1312-
isIPythonMode: this._ipythonMode,
1324+
ipythonMode: this._ipythonMode,
13131325
accessedSymbolSet: new Set<number>(),
13141326
};
13151327
return fileInfo;

packages/pyright-internal/src/languageServerBase.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import { BackgroundAnalysisProgram } from './analyzer/backgroundAnalysisProgram'
8181
import { ImportResolver } from './analyzer/importResolver';
8282
import { MaxAnalysisTime } from './analyzer/program';
8383
import { AnalyzerService, configFileNames } from './analyzer/service';
84+
import { IPythonMode } from './analyzer/sourceFile';
8485
import type { BackgroundAnalysisBase } from './backgroundAnalysisBase';
8586
import { CommandResult } from './commands/commandResult';
8687
import { CancelAfter, CancellationProvider } from './common/cancellationUtils';
@@ -1147,7 +1148,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
11471148
return callItems;
11481149
}
11491150

1150-
protected async onDidOpenTextDocument(params: DidOpenTextDocumentParams, ipythonMode = false) {
1151+
protected async onDidOpenTextDocument(params: DidOpenTextDocumentParams, ipythonMode = IPythonMode.None) {
11511152
const filePath = this._uriParser.decodeTextDocumentUri(params.textDocument.uri);
11521153

11531154
if (!(this.fs as PyrightFileSystem).addUriMap(params.textDocument.uri, filePath)) {
@@ -1164,7 +1165,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
11641165
);
11651166
}
11661167

1167-
protected async onDidChangeTextDocument(params: DidChangeTextDocumentParams, ipythonMode = false) {
1168+
protected async onDidChangeTextDocument(params: DidChangeTextDocumentParams, ipythonMode = IPythonMode.None) {
11681169
this.recordUserInteractionTime();
11691170

11701171
const filePath = this._uriParser.decodeTextDocumentUri(params.textDocument.uri);

packages/pyright-internal/src/languageService/indentationUtils.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Char from 'typescript-char';
1010

1111
import {
1212
findNodeByOffset,
13-
getFirstAncestorOrSelf,
1413
getFirstAncestorOrSelfOfKind,
1514
getStringValueRange,
1615
getTokenAt,
@@ -32,6 +31,7 @@ import {
3231
Token,
3332
TokenType,
3433
} from '../parser/tokenizerTypes';
34+
import { getContainer } from './insertionPointUtils';
3535

3636
interface TokenInfo extends TextRange {
3737
range: Range;
@@ -165,10 +165,7 @@ function _getIndentation(
165165
const suiteSpan = convertTextRangeToRange(suite, parseResults.tokenizerOutput.lines);
166166
if (preferDedent || suiteSpan.start.line === suiteSpan.end.line) {
167167
// Go one more level up.
168-
const outerContainer = getFirstAncestorOrSelf(
169-
suite,
170-
(n) => n !== suite && n.nodeType === ParseNodeType.Suite
171-
) as SuiteNode | undefined;
168+
const outerContainer = getContainer(suite, /*includeSelf*/ false);
172169
return _getIndentationForNode(parseResults, outerContainer ?? parseResults.parseTree, suite);
173170
}
174171

@@ -190,11 +187,7 @@ function _getIndentationForNode(
190187

191188
if (_containsNoIndentBeforeFirstStatement(parseResults, container)) {
192189
const tabSize = _getTabSize(parseResults);
193-
const outerContainer = getFirstAncestorOrSelf(
194-
container,
195-
(n) => n !== container && n.nodeType === ParseNodeType.Suite
196-
) as SuiteNode | undefined;
197-
190+
const outerContainer = getContainer(container, /*includeSelf*/ false);
198191
const result = _getIndentationForNode(parseResults, outerContainer ?? parseResults.parseTree, container);
199192
return {
200193
token: result.token,
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* insertionPointUtils.ts
3+
* Copyright (c) Microsoft Corporation.
4+
* Licensed under the MIT license.
5+
*
6+
* Provides code to get position to inject generated code.
7+
*/
8+
9+
import { getFileInfo, getScope } from '../analyzer/analyzerNodeInfo';
10+
import { Declaration, DeclarationType } from '../analyzer/declaration';
11+
import { getNameNodeForDeclaration } from '../analyzer/declarationUtils';
12+
import { getFirstAncestorOrSelf } from '../analyzer/parseTreeUtils';
13+
import { isPrivateName } from '../analyzer/symbolNameUtils';
14+
import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes';
15+
import { TextRange } from '../common/textRange';
16+
import { ParseNode, ParseNodeType, StatementNode, SuiteNode } from '../parser/parseNodes';
17+
import { ParseResults } from '../parser/parser';
18+
19+
export interface InsertionOptions {
20+
insertBefore?: number;
21+
symbolDeclToIgnore?: string;
22+
}
23+
24+
export function getInsertionPointForSymbolUnderModule(
25+
evaluator: TypeEvaluator,
26+
parseResults: ParseResults,
27+
symbolName: string,
28+
options?: InsertionOptions
29+
): number | undefined {
30+
const module = parseResults.parseTree;
31+
32+
const defaultInsertionPoint = TextRange.getEnd(module);
33+
if (module.statements.length === 0) {
34+
// Empty file.
35+
return defaultInsertionPoint;
36+
}
37+
38+
// See whether same name is already taken.
39+
const scope = getScope(module);
40+
if (!scope) {
41+
// No symbol is defined.
42+
return defaultInsertionPoint;
43+
}
44+
45+
const fileInfo = getFileInfo(module);
46+
const symbol = scope.lookUpSymbol(symbolName);
47+
if (
48+
symbol &&
49+
_getDeclarationsDefinedInCurrentModule(evaluator, symbol.getDeclarations(), fileInfo.filePath, options).length >
50+
0
51+
) {
52+
// Same name symbol is already defined in the module level.
53+
// We can't add another one.
54+
return undefined;
55+
}
56+
57+
if (isPrivateName(symbolName)) {
58+
return Math.max(0, options?.insertBefore ?? defaultInsertionPoint);
59+
}
60+
61+
const lastStatement = _getLastStatementWithPublicName(module.statements);
62+
return TextRange.getEnd(lastStatement);
63+
}
64+
65+
export function getContainer(node: ParseNode, includeSelf = true): SuiteNode | undefined {
66+
return getFirstAncestorOrSelf(node, (n) => {
67+
if (!includeSelf && node === n) {
68+
return false;
69+
}
70+
71+
return n.nodeType === ParseNodeType.Suite;
72+
}) as SuiteNode | undefined;
73+
}
74+
75+
function _getDeclarationsDefinedInCurrentModule(
76+
evaluator: TypeEvaluator,
77+
declarations: Declaration[],
78+
moduleFilePath: string,
79+
options?: InsertionOptions
80+
) {
81+
return declarations.filter((d) => {
82+
const resolved = evaluator.resolveAliasDeclaration(
83+
d,
84+
/*resolveLocalNames*/ true,
85+
/*allowExternallyHiddenAccess*/ true
86+
);
87+
88+
if (!resolved) {
89+
return false;
90+
}
91+
92+
if (options?.symbolDeclToIgnore && resolved.path === options.symbolDeclToIgnore) {
93+
// Even if the symbol is defined in current file, if it is something we are going to remove
94+
// we should ignore the symbol being exist in current file.
95+
// ex) inserting "myFunc" to a file that has "from lib import myFunc"
96+
return false;
97+
}
98+
99+
if (d.type === DeclarationType.Alias) {
100+
const name = getNameNodeForDeclaration(d);
101+
if (!name) {
102+
return false;
103+
}
104+
105+
// Check alias is defined in this module.
106+
const fileInfo = getFileInfo(name);
107+
return fileInfo.filePath === moduleFilePath;
108+
}
109+
110+
return resolved.path === moduleFilePath;
111+
});
112+
}
113+
114+
function _getLastStatementWithPublicName(statements: StatementNode[]) {
115+
let lastStatement = statements[0];
116+
for (let i = 1; i < statements.length; i++) {
117+
const statement = statements[i];
118+
switch (statement.nodeType) {
119+
case ParseNodeType.Class:
120+
case ParseNodeType.Function: {
121+
if (isPrivateName(statement.name.value)) {
122+
return lastStatement;
123+
}
124+
125+
lastStatement = statement;
126+
continue;
127+
}
128+
case ParseNodeType.StatementList: {
129+
if (
130+
statement.statements.some(
131+
(s) =>
132+
s.nodeType === ParseNodeType.Assignment &&
133+
s.leftExpression.nodeType === ParseNodeType.Name &&
134+
isPrivateName(s.leftExpression.value)
135+
)
136+
) {
137+
return lastStatement;
138+
}
139+
140+
lastStatement = statement;
141+
continue;
142+
}
143+
default:
144+
lastStatement = statement;
145+
continue;
146+
}
147+
}
148+
149+
return lastStatement;
150+
}

0 commit comments

Comments
 (0)
0