Compare commits

...

3 commits

Author SHA1 Message Date
Andy Hanson b6b48c2afa Change diagnostic 2018-01-08 13:20:16 -08:00
Andy Hanson 34b9ad5b54 Merge branch 'master' into documentHighlights_modifiers 2018-01-08 12:58:14 -08:00
Andy Hanson 46247157ae Use checker implementation of isLegalModifier 2017-12-13 10:43:17 -08:00
18 changed files with 81 additions and 77 deletions

View file

@ -274,6 +274,18 @@ namespace ts {
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
getAccessibleSymbolChain,
resolveExternalModuleSymbol,
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[] = [];
@ -412,7 +424,7 @@ namespace ts {
const potentialNewTargetCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
const diagnostics = createDiagnosticCollection();
let diagnostics = createDiagnosticCollection();
const enum TypeFacts {
None = 0,
@ -25505,8 +25517,8 @@ namespace ts {
}
switch (modifier.kind) {
case SyntaxKind.ConstKeyword:
if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword));
if (node.kind !== SyntaxKind.EnumDeclaration) {
return grammarErrorOnNode(node, Diagnostics._0_modifier_cannot_be_used_here, tokenToString(SyntaxKind.ConstKeyword));
}
break;
case SyntaxKind.PublicKeyword:

View file

@ -803,10 +803,6 @@
"category": "Error",
"code": 1247
},
"A class member cannot have the '{0}' keyword.": {
"category": "Error",
"code": 1248
},
"A decorator can only decorate a method implementation, not an overload.": {
"category": "Error",
"code": 1249

View file

@ -2316,7 +2316,7 @@ namespace ts {
}
node.decorators = parseDecorators();
node.modifiers = parseModifiers();
node.modifiers = parseModifiers(/*permitConstAsModifier*/ true);
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
// FormalParameter [Yield,Await]:

View file

@ -2876,6 +2876,7 @@ namespace ts {
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
/* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol;
/* @internal */ nodeHasBadModifier(node: Node): boolean;
}
/* @internal */

View file

@ -449,8 +449,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);
@ -3786,8 +3785,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

@ -55,7 +55,7 @@ namespace Harness.Parallel.Worker {
retries() { return this; },
slow() { return this; },
timeout(n) {
timeout = n;
timeout = n as number;
return this;
},
};
@ -127,7 +127,7 @@ namespace Harness.Parallel.Worker {
const fakeContext: Mocha.ITestCallbackContext = {
skip() { return this; },
timeout(n) {
timeout = n;
timeout = n as number;
return this;
},
retries() { return this; },

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);
if (node.parent && (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent))) {
@ -10,7 +11,7 @@ namespace ts.DocumentHighlights {
return [{ fileName: sourceFile.fileName, highlightSpans }];
}
return getSemanticDocumentHighlights(position, node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile);
return getSemanticDocumentHighlights(position, node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile, hasBadModifier);
}
function getHighlightSpanForNode(node: Node, sourceFile: SourceFile): HighlightSpan {
@ -40,8 +41,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;
}
@ -49,7 +50,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:
@ -82,7 +83,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;
}
@ -204,9 +205,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;
}
@ -220,7 +221,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:
@ -228,9 +229,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;
@ -258,33 +258,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

@ -1,9 +1,9 @@
tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts(2,3): error TS1248: A class member cannot have the 'const' keyword.
tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts(2,3): error TS1042: 'const' modifier cannot be used here.
==== tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts (1 errors) ====
class AtomicNumbers {
static const H = 1;
~~~~~~~~~~~~~~~~~~~
!!! error TS1248: A class member cannot have the 'const' keyword.
!!! error TS1042: 'const' modifier cannot be used here.
}

View file

@ -125,6 +125,7 @@ declare namespace FourSlashInterface {
eachMarker(markers: ReadonlyArray<string>, action: (marker: Marker, index: number) => void): void;
eachMarker(action: (marker: Marker, index: number) => 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);