* Bump the npm group with 11 updates Bumps the npm group with 11 updates: | Package | From | To | | --- | --- | --- | | [@actions/artifact](https://github.com/actions/toolkit/tree/HEAD/packages/artifact) | `1.1.1` | `1.1.2` | | [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `1.10.0` | `1.10.1` | | [uuid](https://github.com/uuidjs/uuid) | `9.0.0` | `9.0.1` | | [@types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/uuid) | `9.0.3` | `9.0.4` | | [@types/adm-zip](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/adm-zip) | `0.5.0` | `0.5.1` | | [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) | `4.0.5` | `4.0.6` | | [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver) | `7.5.1` | `7.5.2` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `6.5.0` | `6.7.2` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `6.6.0` | `6.7.2` | | [eslint](https://github.com/eslint/eslint) | `8.48.0` | `8.49.0` | | [sinon](https://github.com/sinonjs/sinon) | `15.2.0` | `16.0.0` | Updates `@actions/artifact` from 1.1.1 to 1.1.2 - [Changelog](https://github.com/actions/toolkit/blob/main/packages/artifact/RELEASES.md) - [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/artifact) Updates `@actions/core` from 1.10.0 to 1.10.1 - [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md) - [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core) Updates `uuid` from 9.0.0 to 9.0.1 - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v9.0.0...v9.0.1) Updates `@types/uuid` from 9.0.3 to 9.0.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/uuid) Updates `@types/adm-zip` from 0.5.0 to 0.5.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/adm-zip) Updates `@types/js-yaml` from 4.0.5 to 4.0.6 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml) Updates `@types/semver` from 7.5.1 to 7.5.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver) Updates `@typescript-eslint/eslint-plugin` from 6.5.0 to 6.7.2 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.7.2/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 6.6.0 to 6.7.2 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.7.2/packages/parser) Updates `eslint` from 8.48.0 to 8.49.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.48.0...v8.49.0) Updates `sinon` from 15.2.0 to 16.0.0 - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md) - [Commits](https://github.com/sinonjs/sinon/compare/v15.2.0...v16.0.0) --- updated-dependencies: - dependency-name: "@actions/artifact" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@actions/core" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@types/uuid" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@types/adm-zip" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@types/js-yaml" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@types/semver" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: sinon dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm ... Signed-off-by: dependabot[bot] <support@github.com> * Update checked-in dependencies --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
293 lines
8.9 KiB
JavaScript
293 lines
8.9 KiB
JavaScript
/**
|
|
* @fileoverview Checks for unreachable code due to return, throws, break, and continue.
|
|
* @author Joel Feenstra
|
|
*/
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @typedef {Object} ConstructorInfo
|
|
* @property {ConstructorInfo | null} upper Info about the constructor that encloses this constructor.
|
|
* @property {boolean} hasSuperCall The flag about having `super()` expressions.
|
|
*/
|
|
|
|
/**
|
|
* Checks whether or not a given variable declarator has the initializer.
|
|
* @param {ASTNode} node A VariableDeclarator node to check.
|
|
* @returns {boolean} `true` if the node has the initializer.
|
|
*/
|
|
function isInitialized(node) {
|
|
return Boolean(node.init);
|
|
}
|
|
|
|
/**
|
|
* Checks all segments in a set and returns true if all are unreachable.
|
|
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
* @returns {boolean} True if all segments are unreachable; false otherwise.
|
|
*/
|
|
function areAllSegmentsUnreachable(segments) {
|
|
|
|
for (const segment of segments) {
|
|
if (segment.reachable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The class to distinguish consecutive unreachable statements.
|
|
*/
|
|
class ConsecutiveRange {
|
|
constructor(sourceCode) {
|
|
this.sourceCode = sourceCode;
|
|
this.startNode = null;
|
|
this.endNode = null;
|
|
}
|
|
|
|
/**
|
|
* The location object of this range.
|
|
* @type {Object}
|
|
*/
|
|
get location() {
|
|
return {
|
|
start: this.startNode.loc.start,
|
|
end: this.endNode.loc.end
|
|
};
|
|
}
|
|
|
|
/**
|
|
* `true` if this range is empty.
|
|
* @type {boolean}
|
|
*/
|
|
get isEmpty() {
|
|
return !(this.startNode && this.endNode);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given node is inside of this range.
|
|
* @param {ASTNode|Token} node The node to check.
|
|
* @returns {boolean} `true` if the node is inside of this range.
|
|
*/
|
|
contains(node) {
|
|
return (
|
|
node.range[0] >= this.startNode.range[0] &&
|
|
node.range[1] <= this.endNode.range[1]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given node is consecutive to this range.
|
|
* @param {ASTNode} node The node to check.
|
|
* @returns {boolean} `true` if the node is consecutive to this range.
|
|
*/
|
|
isConsecutive(node) {
|
|
return this.contains(this.sourceCode.getTokenBefore(node));
|
|
}
|
|
|
|
/**
|
|
* Merges the given node to this range.
|
|
* @param {ASTNode} node The node to merge.
|
|
* @returns {void}
|
|
*/
|
|
merge(node) {
|
|
this.endNode = node;
|
|
}
|
|
|
|
/**
|
|
* Resets this range by the given node or null.
|
|
* @param {ASTNode|null} node The node to reset, or null.
|
|
* @returns {void}
|
|
*/
|
|
reset(node) {
|
|
this.startNode = this.endNode = node;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** @type {import('../shared/types').Rule} */
|
|
module.exports = {
|
|
meta: {
|
|
type: "problem",
|
|
|
|
docs: {
|
|
description: "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
|
|
recommended: true,
|
|
url: "https://eslint.org/docs/latest/rules/no-unreachable"
|
|
},
|
|
|
|
schema: [],
|
|
|
|
messages: {
|
|
unreachableCode: "Unreachable code."
|
|
}
|
|
},
|
|
|
|
create(context) {
|
|
|
|
/** @type {ConstructorInfo | null} */
|
|
let constructorInfo = null;
|
|
|
|
/** @type {ConsecutiveRange} */
|
|
const range = new ConsecutiveRange(context.sourceCode);
|
|
|
|
/** @type {Array<Set<CodePathSegment>>} */
|
|
const codePathSegments = [];
|
|
|
|
/** @type {Set<CodePathSegment>} */
|
|
let currentCodePathSegments = new Set();
|
|
|
|
/**
|
|
* Reports a given node if it's unreachable.
|
|
* @param {ASTNode} node A statement node to report.
|
|
* @returns {void}
|
|
*/
|
|
function reportIfUnreachable(node) {
|
|
let nextNode = null;
|
|
|
|
if (node && (node.type === "PropertyDefinition" || areAllSegmentsUnreachable(currentCodePathSegments))) {
|
|
|
|
// Store this statement to distinguish consecutive statements.
|
|
if (range.isEmpty) {
|
|
range.reset(node);
|
|
return;
|
|
}
|
|
|
|
// Skip if this statement is inside of the current range.
|
|
if (range.contains(node)) {
|
|
return;
|
|
}
|
|
|
|
// Merge if this statement is consecutive to the current range.
|
|
if (range.isConsecutive(node)) {
|
|
range.merge(node);
|
|
return;
|
|
}
|
|
|
|
nextNode = node;
|
|
}
|
|
|
|
/*
|
|
* Report the current range since this statement is reachable or is
|
|
* not consecutive to the current range.
|
|
*/
|
|
if (!range.isEmpty) {
|
|
context.report({
|
|
messageId: "unreachableCode",
|
|
loc: range.location,
|
|
node: range.startNode
|
|
});
|
|
}
|
|
|
|
// Update the current range.
|
|
range.reset(nextNode);
|
|
}
|
|
|
|
return {
|
|
|
|
// Manages the current code path.
|
|
onCodePathStart() {
|
|
codePathSegments.push(currentCodePathSegments);
|
|
currentCodePathSegments = new Set();
|
|
},
|
|
|
|
onCodePathEnd() {
|
|
currentCodePathSegments = codePathSegments.pop();
|
|
},
|
|
|
|
onUnreachableCodePathSegmentStart(segment) {
|
|
currentCodePathSegments.add(segment);
|
|
},
|
|
|
|
onUnreachableCodePathSegmentEnd(segment) {
|
|
currentCodePathSegments.delete(segment);
|
|
},
|
|
|
|
onCodePathSegmentEnd(segment) {
|
|
currentCodePathSegments.delete(segment);
|
|
},
|
|
|
|
onCodePathSegmentStart(segment) {
|
|
currentCodePathSegments.add(segment);
|
|
},
|
|
|
|
// Registers for all statement nodes (excludes FunctionDeclaration).
|
|
BlockStatement: reportIfUnreachable,
|
|
BreakStatement: reportIfUnreachable,
|
|
ClassDeclaration: reportIfUnreachable,
|
|
ContinueStatement: reportIfUnreachable,
|
|
DebuggerStatement: reportIfUnreachable,
|
|
DoWhileStatement: reportIfUnreachable,
|
|
ExpressionStatement: reportIfUnreachable,
|
|
ForInStatement: reportIfUnreachable,
|
|
ForOfStatement: reportIfUnreachable,
|
|
ForStatement: reportIfUnreachable,
|
|
IfStatement: reportIfUnreachable,
|
|
ImportDeclaration: reportIfUnreachable,
|
|
LabeledStatement: reportIfUnreachable,
|
|
ReturnStatement: reportIfUnreachable,
|
|
SwitchStatement: reportIfUnreachable,
|
|
ThrowStatement: reportIfUnreachable,
|
|
TryStatement: reportIfUnreachable,
|
|
|
|
VariableDeclaration(node) {
|
|
if (node.kind !== "var" || node.declarations.some(isInitialized)) {
|
|
reportIfUnreachable(node);
|
|
}
|
|
},
|
|
|
|
WhileStatement: reportIfUnreachable,
|
|
WithStatement: reportIfUnreachable,
|
|
ExportNamedDeclaration: reportIfUnreachable,
|
|
ExportDefaultDeclaration: reportIfUnreachable,
|
|
ExportAllDeclaration: reportIfUnreachable,
|
|
|
|
"Program:exit"() {
|
|
reportIfUnreachable();
|
|
},
|
|
|
|
/*
|
|
* Instance fields defined in a subclass are never created if the constructor of the subclass
|
|
* doesn't call `super()`, so their definitions are unreachable code.
|
|
*/
|
|
"MethodDefinition[kind='constructor']"() {
|
|
constructorInfo = {
|
|
upper: constructorInfo,
|
|
hasSuperCall: false
|
|
};
|
|
},
|
|
"MethodDefinition[kind='constructor']:exit"(node) {
|
|
const { hasSuperCall } = constructorInfo;
|
|
|
|
constructorInfo = constructorInfo.upper;
|
|
|
|
// skip typescript constructors without the body
|
|
if (!node.value.body) {
|
|
return;
|
|
}
|
|
|
|
const classDefinition = node.parent.parent;
|
|
|
|
if (classDefinition.superClass && !hasSuperCall) {
|
|
for (const element of classDefinition.body.body) {
|
|
if (element.type === "PropertyDefinition" && !element.static) {
|
|
reportIfUnreachable(element);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"CallExpression > Super.callee"() {
|
|
if (constructorInfo) {
|
|
constructorInfo.hasSuperCall = true;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|