[parser] Fix scope handling of Flow declared functions (#12735)

This commit is contained in:
Nicolò Ribaudo
2021-02-10 21:07:14 +01:00
committed by GitHub
parent f1a327506e
commit c07185207c
7 changed files with 212 additions and 27 deletions

View File

@@ -5,26 +5,27 @@
// Error messages are colocated with the plugin.
/* eslint-disable @babel/development-internal/dry-error-messages */
import type Parser from "../parser";
import { types as tt, type TokenType } from "../tokenizer/types";
import * as N from "../types";
import type { Options } from "../options";
import type { Pos, Position } from "../util/location";
import type State from "../tokenizer/state";
import { types as tc } from "../tokenizer/context";
import type Parser from "../../parser";
import { types as tt, type TokenType } from "../../tokenizer/types";
import * as N from "../../types";
import type { Pos, Position } from "../../util/location";
import type State from "../../tokenizer/state";
import { types as tc } from "../../tokenizer/context";
import * as charCodes from "charcodes";
import { isIteratorStart, isKeyword } from "../util/identifier";
import { isIteratorStart, isKeyword } from "../../util/identifier";
import FlowScopeHandler from "./scope";
import {
type BindingTypes,
BIND_LEXICAL,
BIND_VAR,
BIND_FUNCTION,
BIND_FLOW_DECLARE_FN,
SCOPE_ARROW,
SCOPE_FUNCTION,
SCOPE_OTHER,
} from "../util/scopeflags";
import type { ExpressionErrors } from "../parser/util";
import { Errors } from "../parser/error";
} from "../../util/scopeflags";
import type { ExpressionErrors } from "../../parser/util";
import { Errors } from "../../parser/error";
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
// to "@flow" or "@noflow" if we see a pragma. Transitions to null if we are
// past the initial comment.
flowPragma: void | null | "flow" | "noflow";
flowPragma: void | null | "flow" | "noflow" = undefined;
constructor(options: ?Options, input: string) {
super(options, input);
this.flowPragma = undefined;
getScopeHandler(): Class<FlowScopeHandler> {
return FlowScopeHandler;
}
shouldParseTypes(): boolean {
@@ -327,6 +327,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.resetEndLocation(id);
this.semicolon();
this.scope.declareName(node.id.name, BIND_FLOW_DECLARE_FN, node.id.start);
return this.finishNode(node, "DeclareFunction");
}

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

View File

@@ -28,20 +28,21 @@ export type ScopeFlags =
// These flags are meant to be _only_ used inside the Scope class (or subclasses).
// prettier-ignore
export const BIND_KIND_VALUE = 0b00000_0000_01,
BIND_KIND_TYPE = 0b00000_0000_10,
export const BIND_KIND_VALUE = 0b000000_0000_01,
BIND_KIND_TYPE = 0b000000_0000_10,
// Used in checkLVal and declareName to determine the type of a binding
BIND_SCOPE_VAR = 0b00000_0001_00, // Var-style binding
BIND_SCOPE_LEXICAL = 0b00000_0010_00, // Let- or const-style binding
BIND_SCOPE_FUNCTION = 0b00000_0100_00, // Function declaration
BIND_SCOPE_OUTSIDE = 0b00000_1000_00, // Special case for function names as
BIND_SCOPE_VAR = 0b000000_0001_00, // Var-style binding
BIND_SCOPE_LEXICAL = 0b000000_0010_00, // Let- or const-style binding
BIND_SCOPE_FUNCTION = 0b000000_0100_00, // Function declaration
BIND_SCOPE_OUTSIDE = 0b000000_1000_00, // Special case for function names as
// bound inside the function
// Misc flags
BIND_FLAGS_NONE = 0b00001_0000_00,
BIND_FLAGS_CLASS = 0b00010_0000_00,
BIND_FLAGS_TS_ENUM = 0b00100_0000_00,
BIND_FLAGS_TS_CONST_ENUM = 0b01000_0000_00,
BIND_FLAGS_TS_EXPORT_ONLY = 0b10000_0000_00;
BIND_FLAGS_NONE = 0b000001_0000_00,
BIND_FLAGS_CLASS = 0b000010_0000_00,
BIND_FLAGS_TS_ENUM = 0b000100_0000_00,
BIND_FLAGS_TS_CONST_ENUM = 0b001000_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
// 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_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 =
| typeof BIND_NONE