analyze-scope.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. "use strict";
  2. const t = require("@babel/types");
  3. const requireFromESLint = require("./require-from-eslint");
  4. const escope = requireFromESLint("eslint-scope");
  5. const Definition = requireFromESLint("eslint-scope/lib/definition").Definition;
  6. const OriginalPatternVisitor = requireFromESLint(
  7. "eslint-scope/lib/pattern-visitor"
  8. );
  9. const OriginalReferencer = requireFromESLint("eslint-scope/lib/referencer");
  10. const fallback = require("eslint-visitor-keys").getKeys;
  11. const childVisitorKeys = require("./visitor-keys");
  12. const flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
  13. "ArrayPattern",
  14. "ClassDeclaration",
  15. "ClassExpression",
  16. "FunctionDeclaration",
  17. "FunctionExpression",
  18. "Identifier",
  19. "ObjectPattern",
  20. "RestElement",
  21. ]);
  22. const visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) {
  23. const value = t.VISITOR_KEYS[key];
  24. if (flowFlippedAliasKeys.indexOf(value) === -1) {
  25. acc[key] = value;
  26. }
  27. return acc;
  28. }, {});
  29. const propertyTypes = {
  30. // loops
  31. callProperties: { type: "loop", values: ["value"] },
  32. indexers: { type: "loop", values: ["key", "value"] },
  33. properties: { type: "loop", values: ["argument", "value"] },
  34. types: { type: "loop" },
  35. params: { type: "loop" },
  36. // single property
  37. argument: { type: "single" },
  38. elementType: { type: "single" },
  39. qualification: { type: "single" },
  40. rest: { type: "single" },
  41. returnType: { type: "single" },
  42. // others
  43. typeAnnotation: { type: "typeAnnotation" },
  44. typeParameters: { type: "typeParameters" },
  45. id: { type: "id" },
  46. };
  47. class PatternVisitor extends OriginalPatternVisitor {
  48. ArrayPattern(node) {
  49. node.elements.forEach(this.visit, this);
  50. }
  51. ObjectPattern(node) {
  52. node.properties.forEach(this.visit, this);
  53. }
  54. }
  55. class Referencer extends OriginalReferencer {
  56. // inherits.
  57. visitPattern(node, options, callback) {
  58. if (!node) {
  59. return;
  60. }
  61. // Visit type annotations.
  62. this._checkIdentifierOrVisit(node.typeAnnotation);
  63. if (t.isAssignmentPattern(node)) {
  64. this._checkIdentifierOrVisit(node.left.typeAnnotation);
  65. }
  66. // Overwrite `super.visitPattern(node, options, callback)` in order to not visit `ArrayPattern#typeAnnotation` and `ObjectPattern#typeAnnotation`.
  67. if (typeof options === "function") {
  68. callback = options;
  69. options = { processRightHandNodes: false };
  70. }
  71. const visitor = new PatternVisitor(this.options, node, callback);
  72. visitor.visit(node);
  73. // Process the right hand nodes recursively.
  74. if (options.processRightHandNodes) {
  75. visitor.rightHandNodes.forEach(this.visit, this);
  76. }
  77. }
  78. // inherits.
  79. visitClass(node) {
  80. // Decorators.
  81. this._visitArray(node.decorators);
  82. // Flow type parameters.
  83. const typeParamScope = this._nestTypeParamScope(node);
  84. // Flow super types.
  85. this._visitTypeAnnotation(node.implements);
  86. this._visitTypeAnnotation(
  87. node.superTypeParameters && node.superTypeParameters.params
  88. );
  89. // Basic.
  90. super.visitClass(node);
  91. // Close the type parameter scope.
  92. if (typeParamScope) {
  93. this.close(node);
  94. }
  95. }
  96. // inherits.
  97. visitFunction(node) {
  98. const typeParamScope = this._nestTypeParamScope(node);
  99. // Flow return types.
  100. this._checkIdentifierOrVisit(node.returnType);
  101. // Basic.
  102. super.visitFunction(node);
  103. // Close the type parameter scope.
  104. if (typeParamScope) {
  105. this.close(node);
  106. }
  107. }
  108. // inherits.
  109. visitProperty(node) {
  110. if (node.value && node.value.type === "TypeCastExpression") {
  111. this._visitTypeAnnotation(node.value);
  112. }
  113. this._visitArray(node.decorators);
  114. super.visitProperty(node);
  115. }
  116. InterfaceDeclaration(node) {
  117. this._createScopeVariable(node, node.id);
  118. const typeParamScope = this._nestTypeParamScope(node);
  119. // TODO: Handle mixins
  120. this._visitArray(node.extends);
  121. this.visit(node.body);
  122. if (typeParamScope) {
  123. this.close(node);
  124. }
  125. }
  126. EnumDeclaration(node) {
  127. this._createScopeVariable(node, node.id);
  128. }
  129. TypeAlias(node) {
  130. this._createScopeVariable(node, node.id);
  131. const typeParamScope = this._nestTypeParamScope(node);
  132. this.visit(node.right);
  133. if (typeParamScope) {
  134. this.close(node);
  135. }
  136. }
  137. ClassProperty(node) {
  138. this._visitClassProperty(node);
  139. }
  140. ClassPrivateProperty(node) {
  141. this._visitClassProperty(node);
  142. }
  143. DeclareModule(node) {
  144. this._visitDeclareX(node);
  145. }
  146. DeclareFunction(node) {
  147. this._visitDeclareX(node);
  148. }
  149. DeclareVariable(node) {
  150. this._visitDeclareX(node);
  151. }
  152. DeclareClass(node) {
  153. this._visitDeclareX(node);
  154. }
  155. // visit OptionalMemberExpression as a MemberExpression.
  156. OptionalMemberExpression(node) {
  157. super.MemberExpression(node);
  158. }
  159. _visitClassProperty(node) {
  160. this._visitTypeAnnotation(node.typeAnnotation);
  161. this.visitProperty(node);
  162. }
  163. _visitDeclareX(node) {
  164. if (node.id) {
  165. this._createScopeVariable(node, node.id);
  166. }
  167. const typeParamScope = this._nestTypeParamScope(node);
  168. if (typeParamScope) {
  169. this.close(node);
  170. }
  171. }
  172. _createScopeVariable(node, name) {
  173. this.currentScope().variableScope.__define(
  174. name,
  175. new Definition("Variable", name, node, null, null, null)
  176. );
  177. }
  178. _nestTypeParamScope(node) {
  179. if (!node.typeParameters) {
  180. return null;
  181. }
  182. const parentScope = this.scopeManager.__currentScope;
  183. const scope = new escope.Scope(
  184. this.scopeManager,
  185. "type-parameters",
  186. parentScope,
  187. node,
  188. false
  189. );
  190. this.scopeManager.__nestScope(scope);
  191. for (let j = 0; j < node.typeParameters.params.length; j++) {
  192. const name = node.typeParameters.params[j];
  193. scope.__define(name, new Definition("TypeParameter", name, name));
  194. if (name.typeAnnotation) {
  195. this._checkIdentifierOrVisit(name);
  196. }
  197. }
  198. scope.__define = function() {
  199. return parentScope.__define.apply(parentScope, arguments);
  200. };
  201. return scope;
  202. }
  203. _visitTypeAnnotation(node) {
  204. if (!node) {
  205. return;
  206. }
  207. if (Array.isArray(node)) {
  208. node.forEach(this._visitTypeAnnotation, this);
  209. return;
  210. }
  211. // get property to check (params, id, etc...)
  212. const visitorValues = visitorKeysMap[node.type];
  213. if (!visitorValues) {
  214. return;
  215. }
  216. // can have multiple properties
  217. for (let i = 0; i < visitorValues.length; i++) {
  218. const visitorValue = visitorValues[i];
  219. const propertyType = propertyTypes[visitorValue];
  220. const nodeProperty = node[visitorValue];
  221. // check if property or type is defined
  222. if (propertyType == null || nodeProperty == null) {
  223. continue;
  224. }
  225. if (propertyType.type === "loop") {
  226. for (let j = 0; j < nodeProperty.length; j++) {
  227. if (Array.isArray(propertyType.values)) {
  228. for (let k = 0; k < propertyType.values.length; k++) {
  229. const loopPropertyNode = nodeProperty[j][propertyType.values[k]];
  230. if (loopPropertyNode) {
  231. this._checkIdentifierOrVisit(loopPropertyNode);
  232. }
  233. }
  234. } else {
  235. this._checkIdentifierOrVisit(nodeProperty[j]);
  236. }
  237. }
  238. } else if (propertyType.type === "single") {
  239. this._checkIdentifierOrVisit(nodeProperty);
  240. } else if (propertyType.type === "typeAnnotation") {
  241. this._visitTypeAnnotation(node.typeAnnotation);
  242. } else if (propertyType.type === "typeParameters") {
  243. for (let l = 0; l < node.typeParameters.params.length; l++) {
  244. this._checkIdentifierOrVisit(node.typeParameters.params[l]);
  245. }
  246. } else if (propertyType.type === "id") {
  247. if (node.id.type === "Identifier") {
  248. this._checkIdentifierOrVisit(node.id);
  249. } else {
  250. this._visitTypeAnnotation(node.id);
  251. }
  252. }
  253. }
  254. }
  255. _checkIdentifierOrVisit(node) {
  256. if (node && node.typeAnnotation) {
  257. this._visitTypeAnnotation(node.typeAnnotation);
  258. } else if (node && node.type === "Identifier") {
  259. this.visit(node);
  260. } else {
  261. this._visitTypeAnnotation(node);
  262. }
  263. }
  264. _visitArray(nodeList) {
  265. if (nodeList) {
  266. for (const node of nodeList) {
  267. this.visit(node);
  268. }
  269. }
  270. }
  271. }
  272. module.exports = function(ast, parserOptions) {
  273. const options = {
  274. ignoreEval: true,
  275. optimistic: false,
  276. directive: false,
  277. nodejsScope:
  278. ast.sourceType === "script" &&
  279. (parserOptions.ecmaFeatures &&
  280. parserOptions.ecmaFeatures.globalReturn) === true,
  281. impliedStrict: false,
  282. sourceType: ast.sourceType,
  283. ecmaVersion: parserOptions.ecmaVersion || 2018,
  284. fallback,
  285. };
  286. options.childVisitorKeys = childVisitorKeys;
  287. const scopeManager = new escope.ScopeManager(options);
  288. const referencer = new Referencer(options, scopeManager);
  289. referencer.visit(ast);
  290. return scopeManager;
  291. };