Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validators for Flow AST node fields #7107

Merged
merged 6 commits into from
Dec 30, 2017

Conversation

bcherny
Copy link
Contributor

@bcherny bcherny commented Dec 26, 2017

Q                       A
Fixed Issues? no
Patch: Bug Fix? YES
Major: Breaking Change? no
Minor: New Feature? no
Tests Added + Pass? YES
Documentation PR no
Any Dependency Changes? no
License MIT

In this PR, we add types to the existing Flow AST nodes. This brings the quality of Flow AST node definitions up to par with existing definitions for TypeScript/Core/Experimental/etc. nodes, and enables:

  • Run time type checking for Flow node fields
  • Compile time type checking for Flow node fields (once we regenerate Flow & TS types)

I tried my best to make sure field type definitions are thorough and complete. I used existing Babel unit tests as my source of truth.

This PR complements, and is independent of, #7101.

@bcherny
Copy link
Contributor Author

bcherny commented Dec 26, 2017

cc @loganfsmyth

@babel-bot
Copy link
Collaborator

babel-bot commented Dec 26, 2017

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/6381/

Copy link
Member

@existentialism existentialism left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work!

Just a couple comments and:

  • We should probably make sure any type's visitor list is up to date with their fields so they can be set in their builder (similar to the DeclareModule comment I left above)
  • Can you run node scripts/generate-babel-types-docs to regenerate @babel/types' README?

},
});

defineType("DeclareExportDeclaration", {
visitor: ["declaration", "specifiers", "source"],
aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"],
fields: {
// todo
declaration: validateOptionalType("TSType"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSType -> Flow

// todo
key: validateType("Identifier"),
value: validateType(["Flow", "Identifier"]),
kind: validate(assertOneOf("init")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"init" | "get" | "set"

id: validateOptionalType("Identifier"),
key: validateType("Flow"),
value: validateType(["Flow", "Identifier"]),
static: validate(assertValueType("boolean")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// todo
id: validateType(["Identifier", "StringLiteral"]),
body: validateType("BlockStatement"),
kind: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to either be:

validateOptional(assertOneOf("CommonJS", "ES"))

or

{
  optional: true,
  validate: assertOneOf("CommonJS", "ES"),
}

typeParameters: validateOptionalType("TypeParameterDeclaration"),
extends: validateOptionalType("InterfaceExtends"),
mixins: validateOptional(arrayOfType("Flow")),
body: validateType("ObjectTypeAnnotation"),
},
});

defineType("DeclareModule", {
visitor: ["id", "body"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add kind to visitor list so we can do something like:

t.declareModule(t.identifier('d'), t.blockStatement([]), "ES")

@bcherny
Copy link
Contributor Author

bcherny commented Dec 26, 2017

@existentialism Thanks for all the comments - very helpful! One question (otherwise comments are all addressed)-

We should probably make sure any type's visitor list is up to date with their fields so they can be set in their builder (similar to the DeclareModule comment I left above)

Currently, it looks like visitor arrays are usually subsets of fields arrays. It sounds like this isn't intentional?

@nicolo-ribaudo
Copy link
Member

It is intentionals, because visitors is an array containing the fields which should be visited by the traverser.

@bcherny
Copy link
Contributor Author

bcherny commented Dec 26, 2017

@nicolo-ribaudo Can you clarify - should visitors equal Object.keys(fields)? Or not always?

});

defineType("ArrayTypeAnnotation", {
visitor: ["elementType"],
aliases: ["Flow"],
fields: {
// todo
elementType: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whould also allow thing like TypeAlias and ClassImplements, but it should only allow types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you use instead? FlowBaseAnnotation?

@existentialism
Copy link
Member

existentialism commented Dec 26, 2017

Yep, that's what I get for reviewing before coffee... I meant to say builder key instead of visitor :)

For ref: https://github.com/babel/babel/blob/master/packages/babel-types/src/definitions/core.js#L48

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many places (I just comented few of them) where you used Flow but it includes too many types. I think that we could introduce a new FlowType alias.

// todo
id: validateType("Identifier"),
typeParameters: validateOptionalType("TypeParameterDeclaration"),
right: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, it should only allow types.

id: validateType("Identifier"),
typeParameters: validateOptionalType("TypeParameterDeclaration"),
extends: validate(arrayOfType("InterfaceExtends")),
mixins: validate(arrayOfType("InterfaceExtends")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for curiosity, what is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

},
});

defineType("DeclareModule", {
visitor: ["id", "body"],
visitor: ["id", "body", "kind"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add kind to visitor list so we can do something like:

t.declareModule(t.identifier('d'), t.blockStatement([]), "ES")

IIRC, the parameters are defined in the builder array. visitor should only contain the nodes which should be traversed.

@nicolo-ribaudo
Copy link
Member

@nicolo-ribaudo Can you clarify - should visitors equal Object.keys(fields)? Or not always?

Not always, it should be something like Object.keys(fields).filter(isNode)

@bcherny
Copy link
Contributor Author

bcherny commented Dec 26, 2017

@existentialism @nicolo-ribaudo All comments are addressed (pending FlowType).

@nicolo-ribaudo Let me know what should go in FlowType, and I can add it.

@nicolo-ribaudo
Copy link
Member

I think it should contain any node whose name ends with TypeAnnotation (but not the TypeAnnotation node itself)

@bcherny
Copy link
Contributor Author

bcherny commented Dec 26, 2017

@nicolo-ribaudo @existentialism Done.

@bcherny
Copy link
Contributor Author

bcherny commented Dec 28, 2017

Can we go ahead and merge this?

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments where I think that FlowType should be used. I also noticed a problem with the name of a field and a place where the type should be Expression instead of Flow.

// todo
id: validateType("Identifier"),
typeParameters: validateOptionalType("TypeParameterDeclaration"),
supertype: validateOptionalType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

typeParameters: validateOptionalType("TypeParameterDeclaration"),
params: validate(arrayOfType("FunctionTypeParam")),
rest: validateOptionalType("FunctionTypeParam"),
returnType: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

},
});

defineType("FunctionTypeParam", {
visitor: ["name", "typeAnnotation"],
aliases: ["Flow"],
fields: {
// todo
name: validateOptionalType("Identifier"),
typeAnnotation: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

fields: {
// todo
types: validate(arrayOfType("Flow")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

fields: {
// todo
typeAnnotation: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

typeAnnotation: {
validate: assertNodeType("Flow"),
},
typeAnnotation: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

},
});

defineType("TypeCastExpression", {
visitor: ["expression", "typeAnnotation"],
aliases: ["Flow", "ExpressionWrapper", "Expression"],
fields: {
// todo
expression: validateType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expression, I think.

},
name: validate(assertValueType("string")),
bound: validateOptionalType("TypeAnnotation"),
default: validateOptionalType("Flow"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

assertEach(assertNodeType("Flow")),
),
},
params: validate(arrayOfType("Flow")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

fields: {
// todo
types: validate(arrayOfType("Flow")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlowType

@existentialism existentialism added the PR: New Feature 🚀 A type of pull request used for our changelog categories label Dec 28, 2017
@bcherny
Copy link
Contributor Author

bcherny commented Dec 29, 2017

@nicolo-ribaudo Done.

@bcherny
Copy link
Contributor Author

bcherny commented Dec 29, 2017

^ Looks like this PR caught a bug in an existing test!

@existentialism existentialism merged commit 4208099 into babel:master Dec 30, 2017
@bcherny bcherny deleted the flow-types2 branch December 31, 2017 18:25
@existentialism existentialism mentioned this pull request Jan 9, 2018
@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Oct 5, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: flow outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: types PR: New Feature 🚀 A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants