Use checker implementation of isLegalModifier

This commit is contained in:
Andy Hanson 2017-12-13 09:06:12 -08:00
parent 79a1240a19
commit 46247157ae
15 changed files with 77 additions and 68 deletions

View file

@ -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;

View file

@ -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]:

View file

@ -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 */

View file

@ -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() {

View file

@ -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);

View file

@ -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[] {

View file

@ -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;

View file

@ -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));

View 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));

View file

@ -8,7 +8,7 @@
/////*2*/const c = 0;
goTo.marker("1");
verify.occurrencesAtPositionCount(0);
verify.occurrencesAtPositionCount(1);
goTo.marker("2");
verify.occurrencesAtPositionCount(0);

View file

@ -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));

View file

@ -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));

View file

@ -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);

View file

@ -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);

View file

@ -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);