[parser] Fix scope handling of Flow declared functions (#12735)
This commit is contained in:
parent
f1a327506e
commit
c07185207c
@ -5,26 +5,27 @@
|
|||||||
// Error messages are colocated with the plugin.
|
// Error messages are colocated with the plugin.
|
||||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||||
|
|
||||||
import type Parser from "../parser";
|
import type Parser from "../../parser";
|
||||||
import { types as tt, type TokenType } from "../tokenizer/types";
|
import { types as tt, type TokenType } from "../../tokenizer/types";
|
||||||
import * as N from "../types";
|
import * as N from "../../types";
|
||||||
import type { Options } from "../options";
|
import type { Pos, Position } from "../../util/location";
|
||||||
import type { Pos, Position } from "../util/location";
|
import type State from "../../tokenizer/state";
|
||||||
import type State from "../tokenizer/state";
|
import { types as tc } from "../../tokenizer/context";
|
||||||
import { types as tc } from "../tokenizer/context";
|
|
||||||
import * as charCodes from "charcodes";
|
import * as charCodes from "charcodes";
|
||||||
import { isIteratorStart, isKeyword } from "../util/identifier";
|
import { isIteratorStart, isKeyword } from "../../util/identifier";
|
||||||
|
import FlowScopeHandler from "./scope";
|
||||||
import {
|
import {
|
||||||
type BindingTypes,
|
type BindingTypes,
|
||||||
BIND_LEXICAL,
|
BIND_LEXICAL,
|
||||||
BIND_VAR,
|
BIND_VAR,
|
||||||
BIND_FUNCTION,
|
BIND_FUNCTION,
|
||||||
|
BIND_FLOW_DECLARE_FN,
|
||||||
SCOPE_ARROW,
|
SCOPE_ARROW,
|
||||||
SCOPE_FUNCTION,
|
SCOPE_FUNCTION,
|
||||||
SCOPE_OTHER,
|
SCOPE_OTHER,
|
||||||
} from "../util/scopeflags";
|
} from "../../util/scopeflags";
|
||||||
import type { ExpressionErrors } from "../parser/util";
|
import type { ExpressionErrors } from "../../parser/util";
|
||||||
import { Errors } from "../parser/error";
|
import { Errors } from "../../parser/error";
|
||||||
|
|
||||||
const reservedTypes = new Set([
|
const reservedTypes = new Set([
|
||||||
"_",
|
"_",
|
||||||
@ -185,11 +186,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
// The value of the @flow/@noflow pragma. Initially undefined, transitions
|
// The value of the @flow/@noflow pragma. Initially undefined, transitions
|
||||||
// to "@flow" or "@noflow" if we see a pragma. Transitions to null if we are
|
// to "@flow" or "@noflow" if we see a pragma. Transitions to null if we are
|
||||||
// past the initial comment.
|
// past the initial comment.
|
||||||
flowPragma: void | null | "flow" | "noflow";
|
flowPragma: void | null | "flow" | "noflow" = undefined;
|
||||||
|
|
||||||
constructor(options: ?Options, input: string) {
|
getScopeHandler(): Class<FlowScopeHandler> {
|
||||||
super(options, input);
|
return FlowScopeHandler;
|
||||||
this.flowPragma = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldParseTypes(): boolean {
|
shouldParseTypes(): boolean {
|
||||||
@ -327,6 +327,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
|||||||
this.resetEndLocation(id);
|
this.resetEndLocation(id);
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
|
|
||||||
|
this.scope.declareName(node.id.name, BIND_FLOW_DECLARE_FN, node.id.start);
|
||||||
|
|
||||||
return this.finishNode(node, "DeclareFunction");
|
return this.finishNode(node, "DeclareFunction");
|
||||||
}
|
}
|
||||||
|
|
||||||
56
packages/babel-parser/src/plugins/flow/scope.js
Normal file
56
packages/babel-parser/src/plugins/flow/scope.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import ScopeHandler, { Scope } from "../../util/scope";
|
||||||
|
import {
|
||||||
|
BIND_FLAGS_FLOW_DECLARE_FN,
|
||||||
|
type ScopeFlags,
|
||||||
|
type BindingTypes,
|
||||||
|
} from "../../util/scopeflags";
|
||||||
|
import * as N from "../../types";
|
||||||
|
|
||||||
|
// Reference implementation: https://github.com/facebook/flow/blob/23aeb2a2ef6eb4241ce178fde5d8f17c5f747fb5/src/typing/env.ml#L536-L584
|
||||||
|
class FlowScope extends Scope {
|
||||||
|
// declare function foo(): type;
|
||||||
|
declareFunctions: string[] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FlowScopeHandler extends ScopeHandler<FlowScope> {
|
||||||
|
createScope(flags: ScopeFlags): FlowScope {
|
||||||
|
return new FlowScope(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
declareName(name: string, bindingType: BindingTypes, pos: number) {
|
||||||
|
const scope = this.currentScope();
|
||||||
|
if (bindingType & BIND_FLAGS_FLOW_DECLARE_FN) {
|
||||||
|
this.checkRedeclarationInScope(scope, name, bindingType, pos);
|
||||||
|
this.maybeExportDefined(scope, name);
|
||||||
|
scope.declareFunctions.push(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.declareName(...arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
isRedeclaredInScope(
|
||||||
|
scope: FlowScope,
|
||||||
|
name: string,
|
||||||
|
bindingType: BindingTypes,
|
||||||
|
): boolean {
|
||||||
|
if (super.isRedeclaredInScope(...arguments)) return true;
|
||||||
|
|
||||||
|
if (bindingType & BIND_FLAGS_FLOW_DECLARE_FN) {
|
||||||
|
return (
|
||||||
|
!scope.declareFunctions.includes(name) &&
|
||||||
|
(scope.lexical.includes(name) || scope.functions.includes(name))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLocalExport(id: N.Identifier) {
|
||||||
|
if (this.scopeStack[0].declareFunctions.indexOf(id.name) === -1) {
|
||||||
|
super.checkLocalExport(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,20 +28,21 @@ export type ScopeFlags =
|
|||||||
|
|
||||||
// These flags are meant to be _only_ used inside the Scope class (or subclasses).
|
// These flags are meant to be _only_ used inside the Scope class (or subclasses).
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const BIND_KIND_VALUE = 0b00000_0000_01,
|
export const BIND_KIND_VALUE = 0b000000_0000_01,
|
||||||
BIND_KIND_TYPE = 0b00000_0000_10,
|
BIND_KIND_TYPE = 0b000000_0000_10,
|
||||||
// Used in checkLVal and declareName to determine the type of a binding
|
// Used in checkLVal and declareName to determine the type of a binding
|
||||||
BIND_SCOPE_VAR = 0b00000_0001_00, // Var-style binding
|
BIND_SCOPE_VAR = 0b000000_0001_00, // Var-style binding
|
||||||
BIND_SCOPE_LEXICAL = 0b00000_0010_00, // Let- or const-style binding
|
BIND_SCOPE_LEXICAL = 0b000000_0010_00, // Let- or const-style binding
|
||||||
BIND_SCOPE_FUNCTION = 0b00000_0100_00, // Function declaration
|
BIND_SCOPE_FUNCTION = 0b000000_0100_00, // Function declaration
|
||||||
BIND_SCOPE_OUTSIDE = 0b00000_1000_00, // Special case for function names as
|
BIND_SCOPE_OUTSIDE = 0b000000_1000_00, // Special case for function names as
|
||||||
// bound inside the function
|
// bound inside the function
|
||||||
// Misc flags
|
// Misc flags
|
||||||
BIND_FLAGS_NONE = 0b00001_0000_00,
|
BIND_FLAGS_NONE = 0b000001_0000_00,
|
||||||
BIND_FLAGS_CLASS = 0b00010_0000_00,
|
BIND_FLAGS_CLASS = 0b000010_0000_00,
|
||||||
BIND_FLAGS_TS_ENUM = 0b00100_0000_00,
|
BIND_FLAGS_TS_ENUM = 0b000100_0000_00,
|
||||||
BIND_FLAGS_TS_CONST_ENUM = 0b01000_0000_00,
|
BIND_FLAGS_TS_CONST_ENUM = 0b001000_0000_00,
|
||||||
BIND_FLAGS_TS_EXPORT_ONLY = 0b10000_0000_00;
|
BIND_FLAGS_TS_EXPORT_ONLY = 0b010000_0000_00,
|
||||||
|
BIND_FLAGS_FLOW_DECLARE_FN = 0b100000_0000_00;
|
||||||
|
|
||||||
// These flags are meant to be _only_ used by Scope consumers
|
// These flags are meant to be _only_ used by Scope consumers
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@ -60,7 +61,9 @@ export const BIND_CLASS = BIND_KIND_VALUE | BIND_KIND_TYPE | BIND_SCOPE_
|
|||||||
BIND_OUTSIDE = BIND_KIND_VALUE | 0 | 0 | BIND_FLAGS_NONE ,
|
BIND_OUTSIDE = BIND_KIND_VALUE | 0 | 0 | BIND_FLAGS_NONE ,
|
||||||
|
|
||||||
BIND_TS_CONST_ENUM = BIND_TS_ENUM | BIND_FLAGS_TS_CONST_ENUM,
|
BIND_TS_CONST_ENUM = BIND_TS_ENUM | BIND_FLAGS_TS_CONST_ENUM,
|
||||||
BIND_TS_NAMESPACE = 0 | 0 | 0 | BIND_FLAGS_TS_EXPORT_ONLY;
|
BIND_TS_NAMESPACE = 0 | 0 | 0 | BIND_FLAGS_TS_EXPORT_ONLY,
|
||||||
|
|
||||||
|
BIND_FLOW_DECLARE_FN = BIND_FLAGS_FLOW_DECLARE_FN;
|
||||||
|
|
||||||
export type BindingTypes =
|
export type BindingTypes =
|
||||||
| typeof BIND_NONE
|
| typeof BIND_NONE
|
||||||
|
|||||||
2
packages/babel-parser/test/fixtures/flow/scope/declare-function-export/input.js
vendored
Normal file
2
packages/babel-parser/test/fixtures/flow/scope/declare-function-export/input.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
declare function foo(): void;
|
||||||
|
export { foo };
|
||||||
61
packages/babel-parser/test/fixtures/flow/scope/declare-function-export/output.json
vendored
Normal file
61
packages/babel-parser/test/fixtures/flow/scope/declare-function-export/output.json
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"type": "File",
|
||||||
|
"start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":15}},
|
||||||
|
"program": {
|
||||||
|
"type": "Program",
|
||||||
|
"start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":15}},
|
||||||
|
"sourceType": "module",
|
||||||
|
"interpreter": null,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "DeclareFunction",
|
||||||
|
"start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}},
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":17,"end":28,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":28},"identifierName":"foo"},
|
||||||
|
"name": "foo",
|
||||||
|
"typeAnnotation": {
|
||||||
|
"type": "TypeAnnotation",
|
||||||
|
"start":20,"end":28,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":28}},
|
||||||
|
"typeAnnotation": {
|
||||||
|
"type": "FunctionTypeAnnotation",
|
||||||
|
"start":20,"end":28,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":28}},
|
||||||
|
"typeParameters": null,
|
||||||
|
"params": [],
|
||||||
|
"rest": null,
|
||||||
|
"returnType": {
|
||||||
|
"type": "VoidTypeAnnotation",
|
||||||
|
"start":24,"end":28,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":28}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"predicate": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ExportNamedDeclaration",
|
||||||
|
"start":30,"end":45,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":15}},
|
||||||
|
"specifiers": [
|
||||||
|
{
|
||||||
|
"type": "ExportSpecifier",
|
||||||
|
"start":39,"end":42,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":12}},
|
||||||
|
"local": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":39,"end":42,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":12},"identifierName":"foo"},
|
||||||
|
"name": "foo"
|
||||||
|
},
|
||||||
|
"exported": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":39,"end":42,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":12},"identifierName":"foo"},
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": null,
|
||||||
|
"declaration": null,
|
||||||
|
"exportKind": "value"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"directives": []
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/babel-parser/test/fixtures/flow/scope/dupl-decl-func-declare-func/input.js
vendored
Normal file
2
packages/babel-parser/test/fixtures/flow/scope/dupl-decl-func-declare-func/input.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
function A() {}
|
||||||
|
declare function A(): void;
|
||||||
59
packages/babel-parser/test/fixtures/flow/scope/dupl-decl-func-declare-func/output.json
vendored
Normal file
59
packages/babel-parser/test/fixtures/flow/scope/dupl-decl-func-declare-func/output.json
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"type": "File",
|
||||||
|
"start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":27}},
|
||||||
|
"errors": [
|
||||||
|
"SyntaxError: Identifier 'A' has already been declared (2:17)"
|
||||||
|
],
|
||||||
|
"program": {
|
||||||
|
"type": "Program",
|
||||||
|
"start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":27}},
|
||||||
|
"sourceType": "module",
|
||||||
|
"interpreter": null,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "FunctionDeclaration",
|
||||||
|
"start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}},
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"A"},
|
||||||
|
"name": "A"
|
||||||
|
},
|
||||||
|
"generator": false,
|
||||||
|
"async": false,
|
||||||
|
"params": [],
|
||||||
|
"body": {
|
||||||
|
"type": "BlockStatement",
|
||||||
|
"start":13,"end":15,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":15}},
|
||||||
|
"body": [],
|
||||||
|
"directives": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "DeclareFunction",
|
||||||
|
"start":16,"end":43,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":27}},
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":33,"end":42,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":26},"identifierName":"A"},
|
||||||
|
"name": "A",
|
||||||
|
"typeAnnotation": {
|
||||||
|
"type": "TypeAnnotation",
|
||||||
|
"start":34,"end":42,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":26}},
|
||||||
|
"typeAnnotation": {
|
||||||
|
"type": "FunctionTypeAnnotation",
|
||||||
|
"start":34,"end":42,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":26}},
|
||||||
|
"typeParameters": null,
|
||||||
|
"params": [],
|
||||||
|
"rest": null,
|
||||||
|
"returnType": {
|
||||||
|
"type": "VoidTypeAnnotation",
|
||||||
|
"start":38,"end":42,"loc":{"start":{"line":2,"column":22},"end":{"line":2,"column":26}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"predicate": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"directives": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user