Throw a syntax error for a declare function with a body (#12054)

This commit is contained in:
Sosuke Suzuki
2020-09-19 07:35:37 +09:00
committed by GitHub
parent 6182001a4c
commit ae18f9c0d9
8 changed files with 196 additions and 33 deletions

View File

@@ -69,6 +69,8 @@ const TSErrors = Object.freeze({
"Type parameters cannot appear on a constructor declaration.",
DeclareClassFieldHasInitializer:
"'declare' class fields cannot have an initializer",
DeclareFunctionHasImplementation:
"An implementation cannot be declared in ambient contexts.",
DuplicateModifier: "Duplicate modifier: '%0'",
EmptyHeritageClauseType: "'%0' list cannot be empty.",
IndexSignatureHasAbstract:
@@ -1469,41 +1471,49 @@ export default (superClass: Class<Parser>): Class<Parser> =>
kind = "let";
}
switch (starttype) {
case tt._function:
return this.parseFunctionStatement(
nany,
/* async */ false,
/* declarationPosition */ true,
);
case tt._class:
// While this is also set by tsParseExpressionStatement, we need to set it
// before parsing the class declaration to now how to register it in the scope.
nany.declare = true;
return this.parseClass(
nany,
/* isStatement */ true,
/* optionalId */ false,
);
case tt._const:
if (this.match(tt._const) && this.isLookaheadContextual("enum")) {
// `const enum = 0;` not allowed because "enum" is a strict mode reserved word.
this.expect(tt._const);
this.expectContextual("enum");
return this.tsParseEnumDeclaration(nany, /* isConst */ true);
}
// falls through
case tt._var:
kind = kind || this.state.value;
return this.parseVarStatement(nany, kind);
case tt.name: {
const value = this.state.value;
if (value === "global") {
return this.tsParseAmbientExternalModuleDeclaration(nany);
} else {
return this.tsParseDeclaration(nany, value, /* next */ true);
const oldIsDeclareContext = this.state.isDeclareContext;
this.state.isDeclareContext = true;
try {
switch (starttype) {
case tt._function:
nany.declare = true;
return this.parseFunctionStatement(
nany,
/* async */ false,
/* declarationPosition */ true,
);
case tt._class:
// While this is also set by tsParseExpressionStatement, we need to set it
// before parsing the class declaration to now how to register it in the scope.
nany.declare = true;
return this.parseClass(
nany,
/* isStatement */ true,
/* optionalId */ false,
);
case tt._const:
if (this.match(tt._const) && this.isLookaheadContextual("enum")) {
// `const enum = 0;` not allowed because "enum" is a strict mode reserved word.
this.expect(tt._const);
this.expectContextual("enum");
return this.tsParseEnumDeclaration(nany, /* isConst */ true);
}
// falls through
case tt._var:
kind = kind || this.state.value;
return this.parseVarStatement(nany, kind);
case tt.name: {
const value = this.state.value;
if (value === "global") {
return this.tsParseAmbientExternalModuleDeclaration(nany);
} else {
return this.tsParseDeclaration(nany, value, /* next */ true);
}
}
}
} finally {
this.state.isDeclareContext = oldIsDeclareContext;
}
}
@@ -1764,6 +1774,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.finishNode(node, bodilessType);
return;
}
if (bodilessType === "TSDeclareFunction" && this.state.isDeclareContext) {
this.raise(node.start, TSErrors.DeclareFunctionHasImplementation);
if (
// $FlowIgnore
node.declare
) {
super.parseFunctionBodyAndFinish(node, bodilessType, isMethod);
return;
}
}
super.parseFunctionBodyAndFinish(node, type, isMethod);
}

View File

@@ -70,6 +70,7 @@ export default class State {
inPropertyName: boolean = false;
hasFlowComment: boolean = false;
isIterator: boolean = false;
isDeclareContext: boolean = false;
// For the smartPipelines plugin:
topicContext: TopicContextState = {