Use checker implementation of isLegalModifier
This commit is contained in:
parent
79a1240a19
commit
46247157ae
|
@ -268,6 +268,18 @@ namespace ts {
|
|||
},
|
||||
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
|
||||
getAccessibleSymbolChain,
|
||||
nodeHasBadModifier: node => {
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
// Make `parseDiagnostics` empty so we actually get grammar errors. But shadow `diagnostics` so grammar errors don't stick.
|
||||
const saveParseDiagnostics = sourceFile.parseDiagnostics;
|
||||
const saveDiagnostics = diagnostics;
|
||||
sourceFile.parseDiagnostics = [];
|
||||
diagnostics = createDiagnosticCollection();
|
||||
const res = checkGrammarModifiers(node);
|
||||
sourceFile.parseDiagnostics = saveParseDiagnostics;
|
||||
diagnostics = saveDiagnostics;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
const tupleTypes: GenericType[] = [];
|
||||
|
@ -405,7 +417,7 @@ namespace ts {
|
|||
const potentialNewTargetCollisions: Node[] = [];
|
||||
const awaitedTypeStack: number[] = [];
|
||||
|
||||
const diagnostics = createDiagnosticCollection();
|
||||
let diagnostics = createDiagnosticCollection();
|
||||
|
||||
const enum TypeFacts {
|
||||
None = 0,
|
||||
|
@ -25403,7 +25415,7 @@ namespace ts {
|
|||
}
|
||||
switch (modifier.kind) {
|
||||
case SyntaxKind.ConstKeyword:
|
||||
if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) {
|
||||
if (node.kind !== SyntaxKind.EnumDeclaration) {
|
||||
return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -2284,7 +2284,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
node.decorators = parseDecorators();
|
||||
node.modifiers = parseModifiers();
|
||||
node.modifiers = parseModifiers(/*permitConstAsModifier*/ true);
|
||||
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
|
||||
// FormalParameter [Yield,Await]:
|
||||
|
|
|
@ -2840,6 +2840,8 @@ namespace ts {
|
|||
* This should be called in a loop climbing parents of the symbol, so we'll get `N`.
|
||||
*/
|
||||
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
|
||||
|
||||
/* @internal */ nodeHasBadModifier(node: Node): boolean;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
|
|
@ -450,8 +450,7 @@ namespace FourSlash {
|
|||
}
|
||||
}
|
||||
|
||||
public goToEachRange(action: () => void) {
|
||||
const ranges = this.getRanges();
|
||||
public goToEachRange(ranges: ReadonlyArray<Range>, action: () => void) {
|
||||
assert(ranges.length);
|
||||
for (const range of ranges) {
|
||||
this.goToRangeStart(range);
|
||||
|
@ -3772,8 +3771,12 @@ namespace FourSlashInterface {
|
|||
this.state.goToRangeStart(range);
|
||||
}
|
||||
|
||||
public eachRange(action: () => void) {
|
||||
this.state.goToEachRange(action);
|
||||
public eachRange(action: () => void): void;
|
||||
public eachRange(ranges: ReadonlyArray<FourSlash.Range>, action: () => void): void;
|
||||
public eachRange(a: {}, b?: {}) {
|
||||
const ranges = b === undefined ? this.state.getRanges() : a as ReadonlyArray<FourSlash.Range>;
|
||||
const action = (b === undefined ? a : b) as () => void;
|
||||
this.state.goToEachRange(ranges, action);
|
||||
}
|
||||
|
||||
public bof() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* @internal */
|
||||
namespace ts.DocumentHighlights {
|
||||
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] | undefined {
|
||||
type HasBadModifier = TypeChecker["nodeHasBadModifier"];
|
||||
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[], hasBadModifier: HasBadModifier): DocumentHighlights[] | undefined {
|
||||
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
// Note that getTouchingWord indicates failure by returning the sourceFile node.
|
||||
if (node === sourceFile) return undefined;
|
||||
|
@ -14,7 +15,7 @@ namespace ts.DocumentHighlights {
|
|||
return [{ fileName: sourceFile.fileName, highlightSpans }];
|
||||
}
|
||||
|
||||
return getSemanticDocumentHighlights(node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile);
|
||||
return getSemanticDocumentHighlights(node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile, hasBadModifier);
|
||||
}
|
||||
|
||||
function getHighlightSpanForNode(node: Node, sourceFile: SourceFile): HighlightSpan {
|
||||
|
@ -44,8 +45,8 @@ namespace ts.DocumentHighlights {
|
|||
return arrayFrom(fileNameToDocumentHighlights.entries(), ([fileName, highlightSpans ]) => ({ fileName, highlightSpans }));
|
||||
}
|
||||
|
||||
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] {
|
||||
const highlightSpans = getHighlightSpans(node, sourceFile);
|
||||
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile, hasBadModifier: HasBadModifier): DocumentHighlights[] {
|
||||
const highlightSpans = getHighlightSpans(node, sourceFile, hasBadModifier);
|
||||
if (!highlightSpans || highlightSpans.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -53,7 +54,7 @@ namespace ts.DocumentHighlights {
|
|||
return [{ fileName: sourceFile.fileName, highlightSpans }];
|
||||
}
|
||||
|
||||
function getHighlightSpans(node: Node, sourceFile: SourceFile): HighlightSpan[] | undefined {
|
||||
function getHighlightSpans(node: Node, sourceFile: SourceFile, hasBadModifier: HasBadModifier): HighlightSpan[] | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.IfKeyword:
|
||||
case SyntaxKind.ElseKeyword:
|
||||
|
@ -86,7 +87,7 @@ namespace ts.DocumentHighlights {
|
|||
return useParent(node.parent, isAccessor, getGetAndSetOccurrences);
|
||||
default:
|
||||
return isModifierKind(node.kind) && (isDeclaration(node.parent) || isVariableStatement(node.parent))
|
||||
? highlightSpans(getModifierOccurrences(node.kind, node.parent))
|
||||
? highlightSpans(getModifierOccurrences(node.kind, node.parent, hasBadModifier))
|
||||
: undefined;
|
||||
}
|
||||
|
||||
|
@ -208,9 +209,9 @@ namespace ts.DocumentHighlights {
|
|||
});
|
||||
}
|
||||
|
||||
function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): Node[] {
|
||||
function getModifierOccurrences(modifier: SyntaxKind, declaration: Declaration | VariableStatement, hasBadModifier: HasBadModifier): Node[] | undefined {
|
||||
// Make sure we only highlight the keyword when it makes sense to do so.
|
||||
if (!isLegalModifier(modifier, declaration)) {
|
||||
if (hasBadModifier(declaration)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ namespace ts.DocumentHighlights {
|
|||
});
|
||||
}
|
||||
|
||||
function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): ReadonlyArray<Node> {
|
||||
function getNodesToSearchForModifier(declaration: Declaration | VariableStatement, modifierFlag: ModifierFlags): ReadonlyArray<Node> {
|
||||
const container = declaration.parent;
|
||||
switch (container.kind) {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
|
@ -232,9 +233,8 @@ namespace ts.DocumentHighlights {
|
|||
case SyntaxKind.Block:
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.DefaultClause:
|
||||
// Container is either a class declaration or the declaration is a classDeclaration
|
||||
if (modifierFlag & ModifierFlags.Abstract) {
|
||||
return [...(<ClassDeclaration>declaration).members, declaration];
|
||||
if (modifierFlag & ModifierFlags.Abstract && isClassDeclaration(declaration)) {
|
||||
return [...declaration.members, declaration];
|
||||
}
|
||||
else {
|
||||
return (<ModuleBlock | SourceFile | Block | CaseClause | DefaultClause>container).statements;
|
||||
|
@ -262,33 +262,6 @@ namespace ts.DocumentHighlights {
|
|||
}
|
||||
}
|
||||
|
||||
function isLegalModifier(modifier: SyntaxKind, declaration: Node): boolean {
|
||||
const container = declaration.parent;
|
||||
switch (modifier) {
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PublicKeyword:
|
||||
switch (container.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
return true;
|
||||
case SyntaxKind.Constructor:
|
||||
return declaration.kind === SyntaxKind.Parameter;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case SyntaxKind.StaticKeyword:
|
||||
return container.kind === SyntaxKind.ClassDeclaration || container.kind === SyntaxKind.ClassExpression;
|
||||
case SyntaxKind.ExportKeyword:
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
return container.kind === SyntaxKind.ModuleBlock || container.kind === SyntaxKind.SourceFile;
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
return container.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.ClassDeclaration;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): boolean {
|
||||
if (token && contains(expected, token.kind)) {
|
||||
keywordList.push(token);
|
||||
|
|
|
@ -1581,7 +1581,7 @@ namespace ts {
|
|||
synchronizeHostData();
|
||||
const sourceFilesToSearch = map(filesToSearch, f => program.getSourceFile(f));
|
||||
const sourceFile = getValidSourceFile(fileName);
|
||||
return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
|
||||
return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch, program.getTypeChecker().nodeHasBadModifier);
|
||||
}
|
||||
|
||||
function getOccurrencesAtPositionCore(fileName: string, position: number): ReferenceEntry[] {
|
||||
|
|
|
@ -124,6 +124,7 @@ declare namespace FourSlashInterface {
|
|||
marker(name?: string | Marker): void;
|
||||
eachMarker(action: () => void): void;
|
||||
rangeStart(range: Range): void;
|
||||
eachRange(ranges: ReadonlyArray<Range>, action: () => void): void;
|
||||
eachRange(action: () => void): void;
|
||||
bof(): void;
|
||||
eof(): void;
|
||||
|
|
|
@ -6,15 +6,9 @@
|
|||
//// [|abstract|] makeSound(): void;
|
||||
////}
|
||||
////// abstract cannot appear here, won't get highlighted
|
||||
////let c = /*1*/abstract class Foo {
|
||||
//// /*2*/abstract foo(): void;
|
||||
////let c = [|abstract|] class Foo {
|
||||
//// [|abstract|] foo(): void;
|
||||
//// abstract bar(): void;
|
||||
////}
|
||||
|
||||
verify.rangesAreOccurrences(false);
|
||||
|
||||
goTo.marker("1");
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
|
||||
goTo.marker("2");
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
goTo.eachRange(() => verify.occurrencesAtPositionCount(0));
|
||||
|
|
14
tests/cases/fourslash/getOccurrencesAsync.ts
Normal file
14
tests/cases/fourslash/getOccurrencesAsync.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[|async|] function f([|async|] p) {}
|
||||
////[|async|] function g() {}
|
||||
|
||||
////[|async|] class C {
|
||||
//// [|async|] m() {}
|
||||
//// [|async|] n() {}
|
||||
////}
|
||||
|
||||
const [f, p, g, C, m, n] = test.ranges();
|
||||
goTo.eachRange([f, g], () => verify.occurrencesAtPositionCount(3));
|
||||
goTo.eachRange([m, n], () => verify.occurrencesAtPositionCount(2));
|
||||
goTo.eachRange([p, C], () => verify.occurrencesAtPositionCount(0));
|
|
@ -8,7 +8,7 @@
|
|||
/////*2*/const c = 0;
|
||||
|
||||
goTo.marker("1");
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
verify.occurrencesAtPositionCount(1);
|
||||
|
||||
goTo.marker("2");
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
|
|
|
@ -10,4 +10,6 @@
|
|||
////declare [|const|] enum E {
|
||||
////}
|
||||
|
||||
goTo.eachRange(() => verify.occurrencesAtPositionCount(0));
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
goTo.eachRange([r0, r2], () => verify.occurrencesAtPositionCount(0));
|
||||
goTo.eachRange([r1, r3], () => verify.occurrencesAtPositionCount(1));
|
||||
|
|
|
@ -10,4 +10,6 @@
|
|||
////export [|const|] enum E {
|
||||
////}
|
||||
|
||||
goTo.eachRange(() => verify.occurrencesAtPositionCount(0));
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
goTo.eachRange([r0, r2], () => verify.occurrencesAtPositionCount(0));
|
||||
goTo.eachRange([r1, r3], () => verify.occurrencesAtPositionCount(1));
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////export const class C {
|
||||
//// private static c/*1*/onst f/*2*/oo;
|
||||
//// constructor(public con/*3*/st foo) {
|
||||
//// private static [|const|] [|foo|];
|
||||
//// constructor(public [|const|] foo) {
|
||||
//// }
|
||||
////}
|
||||
|
||||
goTo.marker("1");
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
goTo.marker("2");
|
||||
const [r0, r1, r2] = test.ranges();
|
||||
goTo.eachRange([r0, r2], () => verify.occurrencesAtPositionCount(0));
|
||||
goTo.rangeStart(r1);
|
||||
verify.occurrencesAtPositionCount(1);
|
||||
goTo.marker("3");
|
||||
verify.occurrencesAtPositionCount(0);
|
|
@ -61,4 +61,8 @@
|
|||
////[|declare|] module dm { }
|
||||
////export class EC { }
|
||||
|
||||
verify.rangesAreOccurrences(false);
|
||||
const [r0, r1, r2, r3] = test.ranges();
|
||||
goTo.eachRange([r0, r1, r3], () => verify.occurrencesAtPositionCount(4));
|
||||
// r2 has a grammar error, so no occurrences
|
||||
goTo.rangeStart(r2);
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
|
|
|
@ -61,4 +61,8 @@
|
|||
////declare module dm { }
|
||||
////[|export|] class EC { }
|
||||
|
||||
verify.rangesAreOccurrences(false);
|
||||
const [r0, r1, r2] = test.ranges();
|
||||
goTo.eachRange([r0, r2], () => verify.occurrencesAtPositionCount(3));
|
||||
// r1 has a grammar error
|
||||
goTo.rangeStart(r1);
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
|
|
Loading…
Reference in a new issue