analyze-scope.js 8.7 KB

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