mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Detect re-exports from "export *" in completions (#20043)
This commit is contained in:
@@ -1703,8 +1703,8 @@ Actual: ${stringify(fullActual)}`);
|
||||
Harness.IO.log(stringify(sigHelp));
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) {
|
||||
const completions = this.getCompletionListAtCaret(options);
|
||||
this.printMembersOrCompletions(completions);
|
||||
}
|
||||
|
||||
@@ -3076,44 +3076,44 @@ Actual: ${stringify(fullActual)}`);
|
||||
hasAction: boolean | undefined,
|
||||
options: FourSlashInterface.VerifyCompletionListContainsOptions | undefined,
|
||||
) {
|
||||
for (const item of items) {
|
||||
if (item.name === entryId.name && item.source === entryId.source) {
|
||||
if (documentation !== undefined || text !== undefined || entryId.source !== undefined) {
|
||||
const details = this.getCompletionEntryDetails(item.name, item.source);
|
||||
const matchingItems = items.filter(item => item.name === entryId.name && item.source === entryId.source);
|
||||
if (matchingItems.length === 0) {
|
||||
const itemsString = items.map(item => stringify({ name: item.name, source: item.source, kind: item.kind })).join(",\n");
|
||||
this.raiseError(`Expected "${stringify({ entryId, text, documentation, kind })}" to be in list [${itemsString}]`);
|
||||
}
|
||||
else if (matchingItems.length > 1 && !(options && options.allowDuplicate)) {
|
||||
this.raiseError(`Found duplicate completion items for ${stringify(entryId)}`);
|
||||
}
|
||||
const item = matchingItems[0];
|
||||
|
||||
if (documentation !== undefined) {
|
||||
assert.equal(ts.displayPartsToString(details.documentation), documentation, this.assertionMessageAtLastKnownMarker("completion item documentation for " + entryId));
|
||||
}
|
||||
if (text !== undefined) {
|
||||
assert.equal(ts.displayPartsToString(details.displayParts), text, this.assertionMessageAtLastKnownMarker("completion item detail text for " + entryId));
|
||||
}
|
||||
if (documentation !== undefined || text !== undefined || entryId.source !== undefined) {
|
||||
const details = this.getCompletionEntryDetails(item.name, item.source);
|
||||
|
||||
if (entryId.source === undefined) {
|
||||
assert.equal(options && options.sourceDisplay, undefined);
|
||||
}
|
||||
else {
|
||||
assert.deepEqual(details.source, [ts.textPart(options!.sourceDisplay)]);
|
||||
}
|
||||
}
|
||||
if (documentation !== undefined) {
|
||||
assert.equal(ts.displayPartsToString(details.documentation), documentation, this.assertionMessageAtLastKnownMarker("completion item documentation for " + entryId));
|
||||
}
|
||||
if (text !== undefined) {
|
||||
assert.equal(ts.displayPartsToString(details.displayParts), text, this.assertionMessageAtLastKnownMarker("completion item detail text for " + entryId));
|
||||
}
|
||||
|
||||
if (kind !== undefined) {
|
||||
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId));
|
||||
}
|
||||
|
||||
if (spanIndex !== undefined) {
|
||||
const span = this.getTextSpanForRangeAtIndex(spanIndex);
|
||||
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId));
|
||||
}
|
||||
|
||||
assert.equal(item.hasAction, hasAction);
|
||||
|
||||
return;
|
||||
if (entryId.source === undefined) {
|
||||
assert.equal(options && options.sourceDisplay, undefined);
|
||||
}
|
||||
else {
|
||||
assert.deepEqual(details.source, [ts.textPart(options!.sourceDisplay)]);
|
||||
}
|
||||
}
|
||||
|
||||
const itemsString = items.map(item => stringify({ name: item.name, source: item.source, kind: item.kind })).join(",\n");
|
||||
if (kind !== undefined) {
|
||||
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId));
|
||||
}
|
||||
|
||||
this.raiseError(`Expected "${stringify({ entryId, text, documentation, kind })}" to be in list [${itemsString}]`);
|
||||
if (spanIndex !== undefined) {
|
||||
const span = this.getTextSpanForRangeAtIndex(spanIndex);
|
||||
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId));
|
||||
}
|
||||
|
||||
assert.equal(item.hasAction, hasAction);
|
||||
}
|
||||
|
||||
private findFile(indexOrName: string | number) {
|
||||
@@ -4358,8 +4358,8 @@ namespace FourSlashInterface {
|
||||
this.state.printCurrentSignatureHelp();
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
this.state.printCompletionListMembers();
|
||||
public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) {
|
||||
this.state.printCompletionListMembers(options);
|
||||
}
|
||||
|
||||
public printAvailableCodeFixes() {
|
||||
@@ -4558,6 +4558,7 @@ namespace FourSlashInterface {
|
||||
|
||||
export interface VerifyCompletionListContainsOptions extends ts.GetCompletionsAtPositionOptions {
|
||||
sourceDisplay: string;
|
||||
allowDuplicate: boolean; // TODO: GH#20042
|
||||
}
|
||||
|
||||
export interface NewContentOptions {
|
||||
|
||||
@@ -1025,6 +1025,14 @@ namespace ts.Completions {
|
||||
|
||||
for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
|
||||
let { name } = symbol;
|
||||
|
||||
// Don't add a completion for a re-export, only for the original.
|
||||
// If `symbol.parent !== moduleSymbol`, this comes from an `export * from "foo"` re-export. Those don't create new symbols.
|
||||
// If `some(...)`, this comes from an `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check).
|
||||
if (symbol.parent !== moduleSymbol || some(symbol.declarations, d => isExportSpecifier(d) && !!d.parent.parent.moduleSpecifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isDefaultExport = name === "default";
|
||||
if (isDefaultExport) {
|
||||
const localSymbol = getLocalSymbolForExportDefault(symbol);
|
||||
@@ -1037,11 +1045,6 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol.declarations && symbol.declarations.some(d => isExportSpecifier(d) && !!d.parent.parent.moduleSpecifier)) {
|
||||
// Don't add a completion for a re-export, only for the original.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stringContainsCharactersInOrder(name.toLowerCase(), tokenTextLowerCase)) {
|
||||
symbols.push(symbol);
|
||||
symbolToOriginInfoMap[getSymbolId(symbol)] = { moduleSymbol, isDefaultExport };
|
||||
|
||||
Reference in New Issue
Block a user