When --noUnusedLocals/--noUnusedParameters is disabled, add suggestions instead of errors (#22361)

* When --noUnusedLocals/--noUnusedParameters is disabled, add suggestions instead of errors

* Improve performance: do not add unused suggestion diagnostics unless asking for a suggestion

* Add "unused" flag to diagnostics

* Code review

* reportsUnused -> reportsUnnecessary

* Fix test
This commit is contained in:
Andy 2018-04-05 12:33:00 -07:00 committed by GitHub
parent f61f12613c
commit 24842b4002
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 335 additions and 200 deletions

View file

@ -65,7 +65,6 @@ namespace ts {
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
@ -313,7 +312,22 @@ namespace ts {
return node && getTypeArgumentConstraint(node);
},
getSuggestionDiagnostics: file => suggestionDiagnostics.get(file.fileName) || emptyArray,
getSuggestionDiagnostics: file => {
return (suggestionDiagnostics.get(file.fileName) || emptyArray).concat(getUnusedDiagnostics());
function getUnusedDiagnostics(): ReadonlyArray<Diagnostic> {
if (file.isDeclarationFile) return emptyArray;
checkSourceFile(file);
const diagnostics: Diagnostic[] = [];
Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
checkUnusedIdentifiers(allPotentiallyUnusedIdentifiers.get(file.fileName)!, (kind, diag) => {
if (!unusedIsError(kind)) {
diagnostics.push({ ...diag, category: DiagnosticCategory.Suggestion });
}
});
return diagnostics;
}
},
};
const tupleTypes: GenericType[] = [];
@ -422,8 +436,9 @@ namespace ts {
let deferredGlobalTemplateStringsArrayType: ObjectType;
let deferredNodes: Node[];
let deferredUnusedIdentifierNodes: Node[];
const seenDeferredUnusedIdentifiers = createMap<true>(); // For assertion that we don't defer the same identifier twice
const allPotentiallyUnusedIdentifiers = createMap<ReadonlyArray<PotentiallyUnusedIdentifier>>(); // key is file name
let potentiallyUnusedIdentifiers: PotentiallyUnusedIdentifier[]; // Potentially unused identifiers in the source file currently being checked.
const seenPotentiallyUnusedIdentifiers = createMap<true>(); // For assertion that we don't defer the same identifier twice
let flowLoopStart = 0;
let flowLoopCount = 0;
@ -455,17 +470,6 @@ namespace ts {
const diagnostics = createDiagnosticCollection();
// Suggestion diagnostics must have a file. Keyed by source file name.
const suggestionDiagnostics = createMultiMap<Diagnostic>();
function addSuggestionDiagnostic(diag: Diagnostic): void {
suggestionDiagnostics.add(diag.file.fileName, { ...diag, category: DiagnosticCategory.Suggestion });
}
function addErrorOrSuggestionDiagnostic(isError: boolean, diag: Diagnostic): void {
if (isError) {
diagnostics.add(diag);
}
else {
addSuggestionDiagnostic(diag);
}
}
const enum TypeFacts {
None = 0,
@ -624,6 +628,12 @@ namespace ts {
resolvedMembers = "resolvedMembers"
}
const enum UnusedKind {
Local,
Parameter,
}
type AddUnusedDiagnostic = (type: UnusedKind, diagnostic: Diagnostic) => void;
const builtinGlobals = createSymbolTable();
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
@ -818,6 +828,18 @@ namespace ts {
diagnostics.add(diagnostic);
}
function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) {
if (isError) {
diagnostics.add(diagnostic);
}
else {
suggestionDiagnostics.add(diagnostic.file.fileName, { ...diagnostic, category: DiagnosticCategory.Suggestion });
}
}
function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message));
}
function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) {
symbolCount++;
const symbol = <TransientSymbol>(new Symbol(flags | SymbolFlags.Transient, name));
@ -1433,7 +1455,7 @@ namespace ts {
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
if (isUse && result && noUnusedIdentifiers && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
result.isReferenced |= meaning;
}
@ -2128,7 +2150,7 @@ namespace ts {
if (sourceFile) {
if (sourceFile.symbol) {
if (resolvedModule.isExternalLibraryImport && !extensionIsTypeScript(resolvedModule.extension)) {
addSuggestionDiagnostic(createModuleImplicitlyAnyDiagnostic(errorNode, resolvedModule, moduleReference));
errorOnImplicitAnyModule(/*isError*/ false, errorNode, resolvedModule, moduleReference);
}
// merged symbol is module declaration symbol combined with all augmentations
return getMergedSymbol(sourceFile.symbol);
@ -2154,7 +2176,7 @@ namespace ts {
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
}
else {
addErrorOrSuggestionDiagnostic(noImplicitAny && !!moduleNotFoundError, createModuleImplicitlyAnyDiagnostic(errorNode, resolvedModule, moduleReference));
errorOnImplicitAnyModule(/*isError*/ noImplicitAny && !!moduleNotFoundError, errorNode, resolvedModule, moduleReference);
}
// Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first.
return undefined;
@ -2179,12 +2201,12 @@ namespace ts {
return undefined;
}
function createModuleImplicitlyAnyDiagnostic(errorNode: Node, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): Diagnostic {
function errorOnImplicitAnyModule(isError: boolean, errorNode: Node, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): void {
const errorInfo = packageId && chainDiagnosticMessages(
/*details*/ undefined,
Diagnostics.Try_npm_install_types_Slash_0_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
getMangledNameForScopedPackage(packageId.name));
return createDiagnosticForNodeFromMessageChain(errorNode, chainDiagnosticMessages(
errorOrSuggestion(isError, errorNode, chainDiagnosticMessages(
errorInfo,
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
moduleReference,
@ -10525,8 +10547,7 @@ namespace ts {
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
// that S and T are contra-variant whereas X and Y are co-variant.
function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary {
const modifiersRelated = relation === comparableRelation || (
relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) :
const modifiersRelated = relation === comparableRelation || (relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) :
getCombinedMappedTypeOptionality(source) <= getCombinedMappedTypeOptionality(target));
if (modifiersRelated) {
let result: Ternary;
@ -16729,7 +16750,7 @@ namespace ts {
}
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) {
if (!prop || !noUnusedIdentifiers || !(prop.flags & SymbolFlags.ClassMember) || !prop.valueDeclaration || !hasModifier(prop.valueDeclaration, ModifierFlags.Private)) {
if (!prop || !(prop.flags & SymbolFlags.ClassMember) || !prop.valueDeclaration || !hasModifier(prop.valueDeclaration, ModifierFlags.Private)) {
return;
}
if (nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly) && !(prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor))) {
@ -19092,7 +19113,6 @@ namespace ts {
}
}
}
registerForUnusedIdentifiersCheck(node);
}
}
@ -20442,8 +20462,8 @@ namespace ts {
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
}
}
if (noUnusedIdentifiers && !(<FunctionDeclaration>node).body) {
checkUnusedTypeParameters(node);
if (node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.JSDocFunctionType) {
registerForUnusedIdentifiersCheck(node);
}
}
}
@ -20644,7 +20664,6 @@ namespace ts {
if (!checkGrammarConstructorTypeParameters(node)) checkGrammarConstructorTypeAnnotation(node);
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
const symbol = getSymbolOfNode(node);
const firstDeclaration = getDeclarationOfKind(symbol, node.kind);
@ -20764,7 +20783,6 @@ namespace ts {
}
}
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
}
function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) {
@ -21915,70 +21933,72 @@ namespace ts {
getReturnTypeOfSignature(getSignatureFromDeclaration(node));
}
}
registerForUnusedIdentifiersCheck(node);
}
function registerForUnusedIdentifiersCheck(node: Node) {
if (deferredUnusedIdentifierNodes) {
function registerForUnusedIdentifiersCheck(node: PotentiallyUnusedIdentifier): void {
// May be in a call such as getTypeOfNode that happened to call this. But potentiallyUnusedIdentifiers is only defined in the scope of `checkSourceFile`.
if (potentiallyUnusedIdentifiers) {
// TODO: GH#22580
// Debug.assert(addToSeen(seenDeferredUnusedIdentifiers, getNodeId(node)), "Deferring unused identifier check twice");
deferredUnusedIdentifierNodes.push(node);
// Debug.assert(addToSeen(seenPotentiallyUnusedIdentifiers, getNodeId(node)), "Adding potentially-unused identifier twice");
potentiallyUnusedIdentifiers.push(node);
}
}
function checkUnusedIdentifiers() {
if (deferredUnusedIdentifierNodes) {
for (const node of deferredUnusedIdentifierNodes) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleDeclaration:
checkUnusedModuleMembers(<ModuleDeclaration | SourceFile>node);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
checkUnusedClassMembers(<ClassDeclaration | ClassExpression>node);
checkUnusedTypeParameters(<ClassDeclaration | ClassExpression>node);
break;
case SyntaxKind.InterfaceDeclaration:
checkUnusedTypeParameters(<InterfaceDeclaration>node);
break;
case SyntaxKind.Block:
case SyntaxKind.CaseBlock:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
checkUnusedLocalsAndParameters(node);
break;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>node).body) {
checkUnusedLocalsAndParameters(<FunctionLikeDeclaration>node);
}
checkUnusedTypeParameters(<FunctionLikeDeclaration>node);
break;
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeAliasDeclaration:
checkUnusedTypeParameters(<MethodSignature | CallSignatureDeclaration | ConstructSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | TypeAliasDeclaration>node);
break;
default:
Debug.fail("Node should not have been registered for unused identifiers check");
}
type PotentiallyUnusedIdentifier =
| SourceFile | ModuleDeclaration | ClassLikeDeclaration | InterfaceDeclaration
| Block | CaseBlock | ForStatement | ForInStatement | ForOfStatement
| Exclude<SignatureDeclaration, IndexSignatureDeclaration | JSDocFunctionType> | TypeAliasDeclaration;
function checkUnusedIdentifiers(potentiallyUnusedIdentifiers: ReadonlyArray<PotentiallyUnusedIdentifier>, addDiagnostic: AddUnusedDiagnostic) {
for (const node of potentiallyUnusedIdentifiers) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleDeclaration:
checkUnusedModuleMembers(node, addDiagnostic);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
checkUnusedClassMembers(node, addDiagnostic);
checkUnusedTypeParameters(node, addDiagnostic);
break;
case SyntaxKind.InterfaceDeclaration:
checkUnusedTypeParameters(node, addDiagnostic);
break;
case SyntaxKind.Block:
case SyntaxKind.CaseBlock:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
checkUnusedLocalsAndParameters(node, addDiagnostic);
break;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
if (node.body) {
checkUnusedLocalsAndParameters(node, addDiagnostic);
}
checkUnusedTypeParameters(node, addDiagnostic);
break;
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeAliasDeclaration:
checkUnusedTypeParameters(node, addDiagnostic);
break;
default:
Debug.assertNever(node, "Node should not have been registered for unused identifiers check");
}
}
}
function checkUnusedLocalsAndParameters(node: Node): void {
if (noUnusedIdentifiers && !(node.flags & NodeFlags.Ambient)) {
function checkUnusedLocalsAndParameters(node: Node, addDiagnostic: AddUnusedDiagnostic): void {
if (!(node.flags & NodeFlags.Ambient)) {
node.locals.forEach(local => {
// If it's purely a type parameter, ignore, will be checked in `checkUnusedTypeParameters`.
// If it's a type parameter merged with a parameter, check if the parameter-side is used.
@ -21986,15 +22006,12 @@ namespace ts {
if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) {
const parameter = <ParameterDeclaration>getRootDeclaration(local.valueDeclaration);
const name = getNameOfDeclaration(local.valueDeclaration);
if (compilerOptions.noUnusedParameters &&
!isParameterPropertyDeclaration(parameter) &&
!parameterIsThisKeyword(parameter) &&
!parameterNameStartsWithUnderscore(name)) {
error(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local));
if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) {
addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
}
}
else if (compilerOptions.noUnusedLocals) {
forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local)));
else {
forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local), addDiagnostic));
}
}
});
@ -22009,7 +22026,7 @@ namespace ts {
return false;
}
function errorUnusedLocal(declaration: Declaration, name: string) {
function errorUnusedLocal(declaration: Declaration, name: string, addDiagnostic: AddUnusedDiagnostic) {
const node = getNameOfDeclaration(declaration) || declaration;
if (isIdentifierThatStartsWithUnderScore(node)) {
const declaration = getRootDeclaration(node.parent);
@ -22020,7 +22037,7 @@ namespace ts {
}
if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
diagnostics.add(createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, Diagnostics._0_is_declared_but_its_value_is_never_read, name));
addDiagnostic(UnusedKind.Local, createDiagnosticForNodeSpan(getSourceFileOfNode(declaration), declaration, node, Diagnostics._0_is_declared_but_its_value_is_never_read, name));
}
}
@ -22032,8 +22049,8 @@ namespace ts {
return isIdentifier(node) && idText(node).charCodeAt(0) === CharacterCodes._;
}
function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void {
if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression, addDiagnostic: AddUnusedDiagnostic): void {
if (!(node.flags & NodeFlags.Ambient)) {
for (const member of node.members) {
switch (member.kind) {
case SyntaxKind.MethodDeclaration:
@ -22046,13 +22063,13 @@ namespace ts {
}
const symbol = getSymbolOfNode(member);
if (!symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) {
error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolToString(symbol));
addDiagnostic(UnusedKind.Local, createDiagnosticForNode(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolToString(symbol)));
}
break;
case SyntaxKind.Constructor:
for (const parameter of (<ConstructorDeclaration>member).parameters) {
if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) {
error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol));
addDiagnostic(UnusedKind.Local, createDiagnosticForNode(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol)));
}
}
break;
@ -22067,27 +22084,23 @@ namespace ts {
}
}
function checkUnusedTypeParameters(node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration) {
if (compilerOptions.noUnusedParameters && !(node.flags & NodeFlags.Ambient)) {
if (node.typeParameters) {
// Only report errors on the last declaration for the type parameter container;
// this ensures that all uses have been accounted for.
const symbol = getSymbolOfNode(node);
const lastDeclaration = symbol && symbol.declarations && lastOrUndefined(symbol.declarations);
if (lastDeclaration !== node) {
return;
}
for (const typeParameter of node.typeParameters) {
if (!(getMergedSymbol(typeParameter.symbol).isReferenced & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) {
error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol));
}
function checkUnusedTypeParameters(
node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration,
addDiagnostic: AddUnusedDiagnostic,
): void {
// Only report errors on the last declaration for the type parameter container;
// this ensures that all uses have been accounted for.
if (!(node.flags & NodeFlags.Ambient) && node.typeParameters && last(getSymbolOfNode(node)!.declarations) === node) {
for (const typeParameter of node.typeParameters) {
if (!(getMergedSymbol(typeParameter.symbol).isReferenced & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) {
addDiagnostic(UnusedKind.Parameter, createDiagnosticForNode(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol)));
}
}
}
}
function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void {
if (compilerOptions.noUnusedLocals && !(node.flags & NodeFlags.Ambient)) {
function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile, addDiagnostic: AddUnusedDiagnostic): void {
if (!(node.flags & NodeFlags.Ambient)) {
// Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
const unusedImports = createMap<[ImportClause, ImportedDeclaration[]]>();
node.locals.forEach(local => {
@ -22106,7 +22119,7 @@ namespace ts {
}
}
else {
errorUnusedLocal(declaration, symbolName(local));
errorUnusedLocal(declaration, symbolName(local), addDiagnostic);
}
}
});
@ -22114,13 +22127,13 @@ namespace ts {
unusedImports.forEach(([importClause, unuseds]) => {
const importDecl = importClause.parent;
if (forEachImportedDeclaration(importClause, d => !contains(unuseds, d))) {
for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name));
for (const unused of unuseds) errorUnusedLocal(unused, idText(unused.name), addDiagnostic);
}
else if (unuseds.length === 1) {
error(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name));
addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics._0_is_declared_but_its_value_is_never_read, idText(first(unuseds).name)));
}
else {
error(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl));
addDiagnostic(UnusedKind.Local, createDiagnosticForNode(importDecl, Diagnostics.All_imports_in_import_declaration_are_unused, showModuleSpecifier(importDecl)));
}
});
}
@ -24878,6 +24891,17 @@ namespace ts {
performance.measure("Check", "beforeCheck", "afterCheck");
}
function unusedIsError(kind: UnusedKind): boolean {
switch (kind) {
case UnusedKind.Local:
return compilerOptions.noUnusedLocals;
case UnusedKind.Parameter:
return compilerOptions.noUnusedParameters;
default:
return Debug.assertNever(kind);
}
}
// Fully type check a source file and collect the relevant diagnostics.
function checkSourceFileWorker(node: SourceFile) {
const links = getNodeLinks(node);
@ -24896,8 +24920,10 @@ namespace ts {
clear(potentialNewTargetCollisions);
deferredNodes = [];
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
flowAnalysisDisabled = false;
if (produceDiagnostics) {
Debug.assert(!allPotentiallyUnusedIdentifiers.has(node.fileName));
allPotentiallyUnusedIdentifiers.set(node.fileName, potentiallyUnusedIdentifiers = []);
}
forEach(node.statements, checkSourceElement);
@ -24907,13 +24933,17 @@ namespace ts {
registerForUnusedIdentifiersCheck(node);
}
if (!node.isDeclarationFile) {
checkUnusedIdentifiers();
if (!node.isDeclarationFile && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters)) {
checkUnusedIdentifiers(potentiallyUnusedIdentifiers, (kind, diag) => {
if (unusedIsError(kind)) {
diagnostics.add(diag);
}
});
}
deferredNodes = undefined;
seenDeferredUnusedIdentifiers.clear();
deferredUnusedIdentifierNodes = undefined;
seenPotentiallyUnusedIdentifiers.clear();
potentiallyUnusedIdentifiers = undefined;
if (isExternalOrCommonJsModule(node)) {
checkExternalModuleExports(node);

View file

@ -1606,6 +1606,7 @@ namespace ts {
messageText: text,
category: message.category,
code: message.code,
reportsUnnecessary: message.unused,
};
}
@ -1635,7 +1636,8 @@ namespace ts {
messageText: text,
category: message.category,
code: message.code
code: message.code,
reportsUnnecessary: message.unused,
};
}
@ -1647,7 +1649,7 @@ namespace ts {
code: chain.code,
category: chain.category,
messageText: chain.next ? chain : chain.messageText
messageText: chain.next ? chain : chain.messageText,
};
}

View file

@ -3282,7 +3282,8 @@
},
"'{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6133
"code": 6133,
"unused": true
},
"Report errors on unused locals.": {
"category": "Message",
@ -3302,7 +3303,8 @@
},
"Property '{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6138
"code": 6138,
"unused": true
},
"Import emit helpers from 'tslib'.": {
"category": "Message",
@ -3514,7 +3516,8 @@
},
"All imports in import declaration are unused.": {
"category": "Error",
"code": 6192
"code": 6192,
"unused": true
},
"Found 1 error.": {
"category": "Message",
@ -3602,7 +3605,8 @@
},
"Unused label.": {
"category": "Error",
"code": 7028
"code": 7028,
"unused": true
},
"Fallthrough case in switch.": {
"category": "Error",

View file

@ -4069,6 +4069,7 @@ namespace ts {
category: DiagnosticCategory;
code: number;
message: string;
unused?: {};
}
/**
@ -4090,6 +4091,8 @@ namespace ts {
length: number | undefined;
messageText: string | DiagnosticMessageChain;
category: DiagnosticCategory;
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
code: number;
source?: string;
}

View file

@ -2577,7 +2577,7 @@ Actual: ${stringify(fullActual)}`);
}
const range = ts.first(ranges);
const codeFixes = this.getCodeFixes(fileName, errorCode, preferences);
const codeFixes = this.getCodeFixes(fileName, errorCode, preferences).filter(f => f.fixId === undefined); // TODO: GH#20315 filter out those that use the import fix ID;
if (codeFixes.length === 0) {
if (expectedTextArray.length !== 0) {

View file

@ -123,13 +123,14 @@ namespace ts {
}
{
const actual = parseJsonConfigFileContent(json, host, basePath, existingOptions, configFileName, resolutionStack);
expected.errors = expected.errors.map<Diagnostic>(error => ({
expected.errors = expected.errors.map((error): Diagnostic => ({
category: error.category,
code: error.code,
file: undefined,
length: undefined,
messageText: error.messageText,
start: undefined,
reportsUnnecessary: undefined,
}));
assertParsed(actual, expected);
}

View file

@ -4068,7 +4068,7 @@ namespace ts.projectSystem {
const folderPath = "/a/b/projects/temp";
const file1: FileOrFolder = {
path: `${folderPath}/a.ts`,
content: 'import f = require("pad")'
content: 'import f = require("pad"); f;'
};
const files = [file1, libFile];
const host = createServerHost(files);

View file

@ -353,11 +353,11 @@ namespace ts.server {
return this.getDiagnostics(file, CommandNames.SuggestionDiagnosticsSync);
}
private getDiagnostics(file: string, command: CommandNames) {
private getDiagnostics(file: string, command: CommandNames): Diagnostic[] {
const request = this.processRequest<protocol.SyntacticDiagnosticsSyncRequest | protocol.SemanticDiagnosticsSyncRequest | protocol.SuggestionDiagnosticsSyncRequest>(command, { file, includeLinePosition: true });
const response = this.processResponse<protocol.SyntacticDiagnosticsSyncResponse | protocol.SemanticDiagnosticsSyncResponse | protocol.SuggestionDiagnosticsSyncResponse>(request);
return (<protocol.DiagnosticWithLinePosition[]>response.body).map(entry => {
return (<protocol.DiagnosticWithLinePosition[]>response.body).map((entry): Diagnostic => {
const category = firstDefined(Object.keys(DiagnosticCategory), id =>
isString(id) && entry.category === id.toLowerCase() ? (<any>DiagnosticCategory)[id] : undefined);
return {
@ -366,7 +366,8 @@ namespace ts.server {
length: entry.length,
messageText: entry.message,
category: Debug.assertDefined(category, "convertDiagnostic: category should not be undefined"),
code: entry.code
code: entry.code,
reportsUnnecessary: entry.reportsUnnecessary,
};
});
}

View file

@ -455,6 +455,8 @@ namespace ts.server.protocol {
endLocation: Location;
category: string;
code: number;
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
}
/**

View file

@ -588,6 +588,7 @@ namespace ts {
length: number;
category: string;
code: number;
unused?: {};
}
export function realizeDiagnostics(diagnostics: ReadonlyArray<Diagnostic>, newLine: string): RealizedDiagnostic[] {
return diagnostics.map(d => realizeDiagnostic(d, newLine));

View file

@ -2271,6 +2271,7 @@ declare namespace ts {
category: DiagnosticCategory;
code: number;
message: string;
unused?: {};
}
/**
* A linked list of formatted diagnostic messages to be used as part of a multiline message.
@ -2290,6 +2291,8 @@ declare namespace ts {
length: number | undefined;
messageText: string | DiagnosticMessageChain;
category: DiagnosticCategory;
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
code: number;
source?: string;
}
@ -5398,6 +5401,8 @@ declare namespace ts.server.protocol {
endLocation: Location;
category: string;
code: number;
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
}
/**
* Response message for "projectInfo" request

View file

@ -2271,6 +2271,7 @@ declare namespace ts {
category: DiagnosticCategory;
code: number;
message: string;
unused?: {};
}
/**
* A linked list of formatted diagnostic messages to be used as part of a multiline message.
@ -2290,6 +2291,8 @@ declare namespace ts {
length: number | undefined;
messageText: string | DiagnosticMessageChain;
category: DiagnosticCategory;
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
code: number;
source?: string;
}

View file

@ -5,6 +5,7 @@
//// * @return {...*}
//// */
//// m(x) {
//// return [x];
//// }
////}
@ -16,6 +17,7 @@ verify.codeFix({
* @return {...*}
*/
m(x): any[] {
return [x];
}
}`,
});

View file

@ -12,6 +12,7 @@
//// * @param {promise<String>} zeta
//// */
////function f(x, y, z, alpha, beta, gamma, delta, epsilon, zeta) {
//// x; y; z; alpha; beta; gamma; delta; epsilon; zeta;
////}
verify.codeFix({
@ -29,5 +30,6 @@ verify.codeFix({
* @param {promise<String>} zeta
*/
function f(x: boolean, y: string, z: number, alpha: object, beta: Date, gamma: Promise<any>, delta: Array<any>, epsilon: Array<number>, zeta: Promise<string>) {
x; y; z; alpha; beta; gamma; delta; epsilon; zeta;
}`,
});

View file

@ -1,12 +1,12 @@
/// <reference path='fourslash.ts' />
// @strict: true
/////** @type {function(*, ...number, ...boolean): void} */
////var x = (x, ys, ...zs) => { };
////var x = (x, ys, ...zs) => { x; ys; zs; };
verify.codeFix({
description: "Annotate with type from JSDoc",
index: 0,
newFileContent:
`/** @type {function(*, ...number, ...boolean): void} */
var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { };`,
var x: (arg0: any, arg1: number[], ...rest: boolean[]) => void = (x, ys, ...zs) => { x; ys; zs; };`,
});

View file

@ -3,7 +3,7 @@
//// /**
//// * @param {number} x - the first parameter
//// */
//// constructor(x) {
//// constructor(readonly x) {
//// }
////}
@ -14,7 +14,7 @@ verify.codeFix({
/**
* @param {number} x - the first parameter
*/
constructor(x: number) {
constructor(readonly x: number) {
}
}`,
});

View file

@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' />
////class C {
//// /** @param {number} value */
//// set c(value) { return 12 }
//// set c(value) { return value }
////}
verify.codeFix({
@ -9,6 +9,6 @@ verify.codeFix({
newFileContent:
`class C {
/** @param {number} value */
set c(value: number) { return 12 }
set c(value: number) { return value }
}`,
});

View file

@ -6,6 +6,7 @@
//// * @param {T} b
//// */
////function f(a, b) {
//// return a || b;
////}
verify.codeFix({
@ -17,5 +18,6 @@ verify.codeFix({
* @param {T} b
*/
function f<T>(a: number, b: T) {
return a || b;
}`,
});

View file

@ -4,11 +4,12 @@
//// * @param {number} a
//// * @param {T} b
//// */
////function /*1*/f<T>(a, b) {
////function f<T>(a, b) {
////}
verify.codeFix({
description: "Annotate with type from JSDoc",
errorCode: 80004, // ignore 'unused T'
newFileContent:
`/**
* @param {number} a

View file

@ -4,19 +4,20 @@
//// * @return {number}
//// */
////function [|f|](x, y) {
//// return x + y;
////}
////
/////**
//// * @return {number}
//// */
////function g(x, y): number {
//// return 0;
//// return x + y;
////}
/////**
//// * @param {number} x
//// */
////function h(x: number, y): number {
//// return 0;
//// return x + y;
////}
////
/////**
@ -24,13 +25,14 @@
//// * @param {string} y
//// */
////function i(x: number, y: string) {
//// return x + y;
////}
/////**
//// * @param {number} x
//// * @return {boolean}
//// */
////function j(x: number, y): boolean {
//// return true;
//// return x < y;
////}
// Only first location triggers a suggestion
@ -41,24 +43,26 @@ verify.getSuggestionDiagnostics([{
verify.codeFix({
description: "Annotate with type from JSDoc",
newFileContent:
errorCode: 80004,
newFileContent:
`/**
* @return {number}
*/
function f(x, y): number {
return x + y;
}
/**
* @return {number}
*/
function g(x, y): number {
return 0;
return x + y;
}
/**
* @param {number} x
*/
function h(x: number, y): number {
return 0;
return x + y;
}
/**
@ -66,12 +70,13 @@ function h(x: number, y): number {
* @param {string} y
*/
function i(x: number, y: string) {
return x + y;
}
/**
* @param {number} x
* @return {boolean}
*/
function j(x: number, y): boolean {
return true;
return x < y;
}`,
});

View file

@ -4,6 +4,7 @@
/////** @param {Object<string, boolean>} sb
//// * @param {Object<number, string>} ns */
////function f(sb, ns) {
//// sb; ns;
////}
verify.codeFix({
@ -13,5 +14,6 @@ verify.codeFix({
/** @param {Object<string, boolean>} sb
* @param {Object<number, string>} ns */
function f(sb: { [s: string]: boolean; }, ns: { [n: number]: string; }) {
sb; ns;
}`,
});

View file

@ -7,6 +7,7 @@
//// * @param {*} beta - I have no idea how this got here
//// */
////function [|f|](x, y, z: string, alpha, beta) {
//// x; y; z; alpha; beta;
////}
verify.getSuggestionDiagnostics([{
@ -17,6 +18,7 @@ verify.getSuggestionDiagnostics([{
verify.codeFix({
description: "Annotate with type from JSDoc",
newFileContent:
// TODO: GH#22358
`/**
* @param {number} x - the first parameter
* @param {{ a: string, b: Date }} y - the most complex parameter
@ -25,5 +27,6 @@ verify.codeFix({
* @param {*} beta - I have no idea how this got here
*/
function f(x: number, y: { a: string; b: Date; }, z: string, alpha, beta: any) {
x; y; z; alpha; beta;
}`,
});

View file

@ -10,6 +10,7 @@
//// * @param {number!} delta
//// */
////function [|f|](x, y, z, alpha, beta, gamma, delta) {
//// x; y; z; alpha; beta; gamma; delta;
////}
verify.codeFix({
@ -25,5 +26,6 @@ verify.codeFix({
* @param {number!} delta
*/
function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { a: string; }, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) {
x; y; z; alpha; beta; gamma; delta;
}`,
});

View file

@ -5,6 +5,7 @@
//// * @returns {number}
//// */
////function f(x) {
//// return x;
////}
verify.codeFix({
@ -15,5 +16,6 @@ verify.codeFix({
* @returns {number}
*/
function f(x: number): number {
return x;
}`,
});

View file

@ -5,6 +5,7 @@
//// * @returns {number}
//// */
////var f = function (x) {
//// return x
////}
verify.codeFix({
@ -15,5 +16,6 @@ verify.codeFix({
* @returns {number}
*/
var f = function (x: number): number {
return x
}`,
});

View file

@ -7,13 +7,14 @@
////not read
// @Filename: /a.ts
/////**/import * as abs from "abs";
////import * as abs from "abs";
////abs;
test.setTypesRegistry({
"abs": undefined,
});
goTo.marker();
goTo.file("/a.ts");
verify.codeFixAvailable([{
description: "Install '@types/abs'",

View file

@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.ts
////import * as abs from "abs";
////import {} from "abs";
test.setTypesRegistry({
"abs": undefined,

View file

@ -7,6 +7,7 @@
// @Filename: /a.ts
////import * as abs from [|"abs/subModule"|];
////abs;
test.setTypesRegistry({
"abs": undefined,

View file

@ -8,6 +8,7 @@
// @Filename: /a.js
////import abs from [|"abs"|];
////abs;
test.setTypesRegistry({ "abs": undefined });

View file

@ -1,9 +1,9 @@
/// <reference path='fourslash.ts' />
////interface I<X> { x: X}
////[|class C<T extends string , U> extends I<T>|]{}
////interface I<X, Y> { x: X; y: Y; }
////[|class C<T extends string , U> extends I<T , U>|]{}
verify.codeFix({
description: "Change 'extends' to 'implements'",
newRangeContent: "class C<T extends string , U> implements I<T>",
newRangeContent: "class C<T extends string , U> implements I<T , U>",
});

View file

@ -1,5 +1,5 @@
/// <reference path='fourslash.ts' />
//// var index = { set p(x: [|*|]) { } };
//// var index = { set p(x: [|*|]) { x; } };
verify.codeFix({
description: "Change '*' to 'any'",

View file

@ -1,6 +1,7 @@
/// <reference path='fourslash.ts' />
////function foo<T>(a: T) {
//// a;
//// abstract class C<U> {
//// abstract a: T | U;
//// }
@ -14,6 +15,7 @@ verify.codeFix({
// TODO: GH#18795
newFileContent:
`function foo<T>(a: T) {
a;
abstract class C<U> {
abstract a: T | U;
}

View file

@ -1,6 +1,7 @@
/// <reference path='fourslash.ts' />
////function foo<T>(a: T) {
//// a;
//// abstract class C<U> {
//// abstract a: T | U;
//// }
@ -14,6 +15,7 @@ verify.codeFix({
// TODO: GH#18795
newFileContent:
`function foo<T>(a: T) {
a;
abstract class C<U> {
abstract a: T | U;
}

View file

@ -1,8 +1,6 @@
/// <reference path='fourslash.ts' />
////abstract class A {
//// private _a: string;
////
//// abstract get a(): number | string;
//// abstract get b(): this;
//// abstract get c(): A;
@ -25,8 +23,6 @@ verify.codeFix({
// TODO: GH#18795
newFileContent:
`abstract class A {
private _a: string;
abstract get a(): number | string;
abstract get b(): this;
abstract get c(): A;

View file

@ -2,6 +2,7 @@
//// abstract class A {
//// private abstract x: number;
//// m() { this.x; } // Avoid unused private
//// }
////
//// class C extends A {[| |]}

View file

@ -1,8 +1,6 @@
/// <reference path='fourslash.ts' />
////abstract class A {
//// private _a: string;
////
//// abstract get a(): string;
//// abstract set a(newName: string);
////
@ -17,8 +15,6 @@ verify.codeFix({
description: "Implement interface 'A'",
newFileContent:
`abstract class A {
private _a: string;
abstract get a(): string;
abstract set a(newName: string);

View file

@ -2,7 +2,7 @@
////class A {
//// method(a: number, b: string): boolean;
//// method(a: string | number, b?: string | number): boolean | Function { return true; }
//// method(a: string | number, b?: string | number): boolean | Function { return a + b as any; }
////}
////class C implements A {}
@ -11,7 +11,7 @@ verify.codeFix({
newFileContent:
`class A {
method(a: number, b: string): boolean;
method(a: string | number, b?: string | number): boolean | Function { return true; }
method(a: string | number, b?: string | number): boolean | Function { return a + b as any; }
}
class C implements A {
method(a: number, b: string): boolean;

View file

@ -4,7 +4,7 @@
//// method(a: any, b: string): boolean;
//// method(a: string, b: number): Function;
//// method(a: string): Function;
//// method(a: string | number, b?: string | number): boolean | Function { return true; }
//// method(a: string | number, b?: string | number): boolean | Function { return a + b as any; }
////}
////class C implements A {[| |]}
@ -15,7 +15,7 @@ verify.codeFix({
method(a: any, b: string): boolean;
method(a: string, b: number): Function;
method(a: string): Function;
method(a: string | number, b?: string | number): boolean | Function { return true; }
method(a: string | number, b?: string | number): boolean | Function { return a + b as any; }
}
class C implements A {
method(a: any, b: string): boolean;

View file

@ -5,6 +5,7 @@
//// private y: number;
//// protected z: number;
//// public w: number;
//// public useY() { this.y; }
////}
////
////class C implements A {[| |]}
@ -17,11 +18,15 @@ verify.codeFix({
private y: number;
protected z: number;
public w: number;
public useY() { this.y; }
}
class C implements A {
x: number;
protected z: number;
public w: number;
public useY(): void {
throw new Error("Method not implemented.");
}
}`,
});

View file

@ -2,7 +2,7 @@
////abstract class C1 { }
////abstract class C2 {
//// abstract f<T extends number>();
//// abstract f<T extends number>(): T;
////}
////interface I1 extends C1, C2 { }
////class C3 implements I1 {[| |]}
@ -12,11 +12,11 @@ verify.codeFix({
newFileContent:
`abstract class C1 { }
abstract class C2 {
abstract f<T extends number>();
abstract f<T extends number>(): T;
}
interface I1 extends C1, C2 { }
class C3 implements I1 {
f<T extends number>() {
f<T extends number>(): T {
throw new Error("Method not implemented.");
}
}`,

View file

@ -8,6 +8,7 @@
//// this.a = 12;
//// super();
//// |]}
//// m() { this.a; } // avoid unused 'a'
////}
verify.rangeAfterCodeFix(`
super();

View file

@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' />
////class Base{
//// constructor(id: number) { }
//// constructor(id: number) { id; }
////}
////class C extends Base{
//// constructor(private a:number) {

View file

@ -3,6 +3,6 @@
//// export interface Foo {
//// bar: string;
//// }
//// const x: [|Foo.bar|] = ""
//// export const x: [|Foo.bar|] = ""
verify.rangeAfterCodeFix(`Foo["bar"]`);

View file

@ -1,13 +1,13 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
////function f1(a) { }
////function f1(a) { a; }
////function h1() {
//// class C { p: number };
//// f1({ ofTypeC: new C() });
////}
////
////function f2(a) { }
////function f2(a) { a; }
////function h2() {
//// interface I { a: number }
//// var i: I = {a : 1};

View file

@ -2,6 +2,7 @@
// @noImplicitAny: true
////function f([|a? |]){
//// a;
////}
////f();
////f(1);

View file

@ -2,6 +2,7 @@
// @noImplicitAny: true
////function f(a: number, [|...rest |]){
//// a; rest;
////}
////f(1);
////f(2, "s1");

View file

@ -2,6 +2,7 @@
// @noImplicitAny: true
////function f(a: number, [|...rest |]){
//// a; rest;
////}
////f(1);
////f(2, "s1");

View file

@ -2,6 +2,7 @@
// @noImplicitAny: true
////function f(a: number, [|...rest |]){
//// a;
//// rest.push(22);
////}

View file

@ -3,6 +3,7 @@
// @noImplicitAny: true
////class C {
//// set [|x(v)|] {
//// v;
//// }
////}
////(new C).x = 1;

View file

@ -3,6 +3,7 @@
// @noImplicitAny: true
////class C {
//// set [|x(v)|] {
//// v;
//// }
////}
////(new C).x = 1;

View file

@ -8,7 +8,7 @@
// @Filename: /b.ts
////export class C {
//// [|set x(val) {}|]
//// [|set x(val) { val; }|]
//// method() { this.x = import("./a"); }
////}

View file

@ -4,7 +4,7 @@
////export const fooooooooo = 1;
// @Filename: f2.ts
////import {[|fooooooooa|]} from "./f1";
////import {[|fooooooooa|]} from "./f1"; fooooooooa;
goTo.file("f2.ts")
verify.rangeAfterCodeFix(`fooooooooo`);

View file

@ -0,0 +1,25 @@
/// <reference path='fourslash.ts' />
////function f([|p|]) {
//// const [|x|] = 0;
////}
const [r0, r1] = test.ranges();
verify.getSuggestionDiagnostics([
{
message: "'p' is declared but its value is never read.",
range: r0,
code: 6133,
unused: true,
},
{
message: "'x' is declared but its value is never read.",
range: r1,
code: 6133,
unused: true,
}
]);
verify.codeFixAvailable(
["Remove declaration for: 'p'", "Prefix 'p' with an underscore", "Remove declaration for: 'x'"]
.map(description => ({ description })));

View file

@ -8,15 +8,18 @@
// @Filename: /b.ts
/////*com ment*/import * as [|a|] from "./a";/*tnem moc*/
////a;
// @Filename: /c.ts
/////*com ment*/import [|a|] = require("./a");/*tnem moc*/
////a;
// @Filename: /d.ts
////import "./a";
// @Filename: /e.ts
////import * as n from "./non-existant";
////n;
for (const file of ["/b.ts", "/c.ts"]) {
goTo.file(file);
@ -29,7 +32,9 @@ for (const file of ["/b.ts", "/c.ts"]) {
verify.codeFix({
description: "Convert to default import",
newFileContent: `/*com ment*/import a from "./a";/*tnem moc*/`,
newFileContent:
`/*com ment*/import a from "./a";/*tnem moc*/
a;`,
});
}

View file

@ -17,7 +17,7 @@
/////**
//// * This is a cool function!
////*/
////fn.prototype.bar = function (x, y, z) {
////fn.prototype.bar = function (y) {
//// this.x = y;
////};
@ -38,7 +38,7 @@ verify.codeFix({
/**
* This is a cool function!
*/
bar(x, y, z) {
bar(y) {
this.x = y;
}
}

View file

@ -518,6 +518,7 @@ declare namespace FourSlashInterface {
/** @default `test.ranges()[0]` */
range?: Range;
code: number;
unused?: true;
}
interface VerifyDocumentHighlightsOptions {
filesToSearch?: ReadonlyArray<string>;

View file

@ -1,9 +1,10 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function f(/*1*/x) {
//// }
//// f(
////function f(/*1*/x) {
//// x;
////}
////f(
verify.not.codeFixAvailable([]);

View file

@ -1,7 +1,9 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function f(new C(100, 3, undefined)
verify.not.codeFixAvailable([]);
////function f(new C(100, 3, undefined)
verify.codeFix({
description: "Prefix 'C' with an underscore",
newFileContent: "function f(new _C(100, 3, undefined)",
});

View file

@ -11,10 +11,12 @@
////exports.y;
////
////exports.z = 2;
////function f(z) {
//// exports.z;
////exports.f = function(z) {
//// z;
////}
// TODO: GH#22492 Should be a able access `exports.z` inside `exports.f`
verify.codeFix({
description: "Convert to ES6 module",
newFileContent:
@ -26,9 +28,8 @@ const _y = y;
export { _y as y };
_y;
const _z = 2;
export { _z as z };
function f(z) {
_z;
export const z = 2;
export function f(z) {
z;
}`,
});

View file

@ -3,13 +3,13 @@
// @allowJs: true
// @Filename: /a.js
////exports.f = async function* f(p) {}
////exports.f = async function* f(p) { p; }
////exports.C = class C extends D { m() {} }
verify.codeFix({
description: "Convert to ES6 module",
newFileContent:
`export async function* f(p) { }
`export async function* f(p) { p; }
export class C extends D {
m() { }
}`,

View file

@ -4,9 +4,11 @@
// @Filename: /a.js
////const [x, y] = /*a*/require/*b*/("x");
////x; y;
verify.codeFix({
description: "Convert to ES6 module",
newFileContent: `import _x from "x";
const [x, y] = _x;`,
const [x, y] = _x;
x; y;`,
});

View file

@ -6,6 +6,7 @@
////const x = require("x");
////const [a, b] = require("x");
////const {c, ...d} = require("x");
////x; a; b; c; d;
verify.codeFix({
description: "Convert to ES6 module",
@ -14,5 +15,6 @@ verify.codeFix({
import _x from "x";
const [a, b] = _x;
import __x from "x";
const { c, ...d } = __x;`,
const { c, ...d } = __x;
x; a; b; c; d;`,
});

View file

@ -6,11 +6,13 @@
// @Filename: /a.js
////const x = require("x"), y = 0, { z } = require("z");
////x; y; z;
verify.codeFix({
description: "Convert to ES6 module",
newFileContent:
`import x from "x";
const y = 0;
import { z } from "z";`,
import { z } from "z";
x; y; z;`,
});

View file

@ -4,10 +4,12 @@
// @Filename: /a.js
////const { x: { a, b } } = require("x");
////a; b;
verify.codeFix({
description: "Convert to ES6 module",
newFileContent:
`import x from "x";
const { x: { a, b } } = x;`,
const { x: { a, b } } = x;
a; b;`,
});

View file

@ -4,8 +4,11 @@
// @Filename: /a.js
////const { x, y: z } = require("x");
////x; z;
verify.codeFix({
description: "Convert to ES6 module",
newFileContent: 'import { x, y as z } from "x";',
newFileContent:
`import { x, y as z } from "x";
x; z;`,
});

View file

@ -8,6 +8,7 @@
////const a = require("a").a;
////const [a, b] = require("c").d;
////const [a, b] = require("c").a; // Test that we avoid shadowing the earlier local variable 'a' from 'const [a,b] = d;'.
////x; a; b;
verify.codeFix({
description: "Convert to ES6 module",
@ -18,5 +19,6 @@ import { a } from "a";
import { d } from "c";
const [a, b] = d;
import { a as _a } from "c";
const [a, b] = _a; // Test that we avoid shadowing the earlier local variable 'a' from 'const [a,b] = d;'.`,
const [a, b] = _a; // Test that we avoid shadowing the earlier local variable 'a' from 'const [a,b] = d;'.
x; a; b;`,
});

View file

@ -2,9 +2,9 @@
// @allowJs: true
// @Filename: /a.js
////const a = require('a');
////const a = require('a'); a;
verify.codeFix({
description: "Convert to ES6 module",
newFileContent: "import a from 'a';",
newFileContent: "import a from 'a'; a;",
});