Skip to content

Commit 52ec508

Browse files
paulvanbrenkzhengbli
authored andcommitted
Code fix for missing imports (#11768)
* Add codefix for missing imports + tests * Re-order and cleanup * refactor * make tests pass * Make import specifier for new imports more comprehensive * Fix existing import cases * refactor * Fix multiple import statement case * add multiple code fixes and code action filtering and polishing * not using the generic verify method for import fixes. * Correct insert position for new imports * improve the code action filtering logic * Fix line ending issue * cache where we can
1 parent 0b0b68e commit 52ec508

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1265
-9
lines changed

src/compiler/checker.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ namespace ts {
104104
getEmitResolver,
105105
getExportsOfModule: getExportsOfModuleAsArray,
106106
getAmbientModules,
107-
108107
getJsxElementAttributesType,
109108
getJsxIntrinsicTagNames,
110109
isOptionalParameter,
110+
tryGetMemberInModuleExports,
111111
tryFindAmbientModuleWithoutAugmentations: moduleName => {
112112
// we deliberately exclude augmentations
113113
// since we are only interested in declarations of the module itself
@@ -1483,6 +1483,13 @@ namespace ts {
14831483
return symbolsToArray(getExportsOfModule(moduleSymbol));
14841484
}
14851485

1486+
function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined {
1487+
const symbolTable = getExportsOfModule(moduleSymbol);
1488+
if (symbolTable) {
1489+
return symbolTable[memberName];
1490+
}
1491+
}
1492+
14861493
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
14871494
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols;
14881495
}

src/compiler/core.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,14 @@ namespace ts {
13911391
getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS;
13921392
}
13931393

1394+
export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) {
1395+
let moduleResolution = compilerOptions.moduleResolution;
1396+
if (moduleResolution === undefined) {
1397+
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
1398+
}
1399+
return moduleResolution;
1400+
}
1401+
13941402
/* @internal */
13951403
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
13961404
let seenAsterisk = false;

src/compiler/diagnosticMessages.json

+12
Original file line numberDiff line numberDiff line change
@@ -3190,5 +3190,17 @@
31903190
"Type '{0}' is not assignable to type '{1}'. Two different types with this name exist, but they are unrelated.": {
31913191
"category": "Error",
31923192
"code": 90010
3193+
},
3194+
"Import {0} from {1}": {
3195+
"category": "Message",
3196+
"code": 90013
3197+
},
3198+
"Change {0} to {1}": {
3199+
"category": "Message",
3200+
"code": 90014
3201+
},
3202+
"Add {0} to existing import declaration from {1}": {
3203+
"category": "Message",
3204+
"code": 90015
31933205
}
31943206
}

src/compiler/moduleNameResolver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace ts {
6161
return { resolvedModule: resolved && resolvedModuleFromResolved(resolved, isExternalLibraryImport), failedLookupLocations };
6262
}
6363

64-
function moduleHasNonRelativeName(moduleName: string): boolean {
64+
export function moduleHasNonRelativeName(moduleName: string): boolean {
6565
return !(isRootedDiskPath(moduleName) || isExternalModuleNameRelative(moduleName));
6666
}
6767

src/compiler/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2371,6 +2371,8 @@ namespace ts {
23712371
isOptionalParameter(node: ParameterDeclaration): boolean;
23722372
getAmbientModules(): Symbol[];
23732373

2374+
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
2375+
23742376
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol;
23752377

23762378
// Should not be called directly. Should only be accessed through the Program instance.
@@ -3190,7 +3192,7 @@ namespace ts {
31903192
target?: ScriptTarget;
31913193
traceResolution?: boolean;
31923194
types?: string[];
3193-
/** Paths used to used to compute primary types search locations */
3195+
/** Paths used to compute primary types search locations */
31943196
typeRoots?: string[];
31953197
/*@internal*/ version?: boolean;
31963198
/*@internal*/ watch?: boolean;

src/harness/fourslash.ts

+37-1
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,34 @@ namespace FourSlash {
20462046
}
20472047
}
20482048

2049+
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
2050+
const ranges = this.getRanges();
2051+
if (ranges.length == 0) {
2052+
this.raiseError("At least one range should be specified in the testfile.");
2053+
}
2054+
2055+
const codeFixes = this.getCodeFixes(errorCode);
2056+
2057+
if (!codeFixes || codeFixes.length == 0) {
2058+
this.raiseError("No codefixes returned.");
2059+
}
2060+
2061+
const actualTextArray: string[] = [];
2062+
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(codeFixes[0].changes[0].fileName);
2063+
const originalContent = scriptInfo.content;
2064+
for (const codeFix of codeFixes) {
2065+
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
2066+
actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0])));
2067+
scriptInfo.updateContent(originalContent);
2068+
}
2069+
const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort();
2070+
const sortedActualArray = actualTextArray.sort();
2071+
if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
2072+
this.raiseError(
2073+
`Actual text array doesn't match expected text array. \nActual: \n"${sortedActualArray.join("\n\n")}"\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
2074+
}
2075+
}
2076+
20492077
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
20502078
const name = "verifyDocCommentTemplate";
20512079
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
@@ -2079,6 +2107,10 @@ namespace FourSlash {
20792107
});
20802108
}
20812109

2110+
private normalizeNewlines(str: string) {
2111+
return str.replace(/\r?\n/g, "\n");
2112+
}
2113+
20822114
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
20832115

20842116
const openBraceMap = ts.createMap<ts.CharacterCodes>({
@@ -2606,7 +2638,7 @@ ${code}
26062638
resetLocalData();
26072639
}
26082640

2609-
currentFileName = basePath + "/" + value;
2641+
currentFileName = ts.isRootedDiskPath(value) ? value : basePath + "/" + value;
26102642
currentFileOptions[key] = value;
26112643
}
26122644
else {
@@ -3303,6 +3335,10 @@ namespace FourSlashInterface {
33033335
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
33043336
}
33053337

3338+
public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
3339+
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
3340+
}
3341+
33063342
public navigationBar(json: any) {
33073343
this.state.verifyNavigationBar(json);
33083344
}

src/services/codefixes/codeFixProvider.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* @internal */
1+
/* @internal */
22
namespace ts {
33
export interface CodeFix {
44
errorCodes: number[];
@@ -11,6 +11,8 @@ namespace ts {
1111
span: TextSpan;
1212
program: Program;
1313
newLineCharacter: string;
14+
host: LanguageServiceHost;
15+
cancellationToken: CancellationToken;
1416
}
1517

1618
export namespace codefix {

src/services/codefixes/fixes.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
///<reference path='superFixes.ts' />
2-
///<reference path='unusedIdentifierFixes.ts' />
2+
///<reference path='importFixes.ts' />
3+
///<reference path='unusedIdentifierFixes.ts' />

0 commit comments

Comments
 (0)