-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: make @agoric/eslint-plugin deal with assert.fail as throw
- Loading branch information
1 parent
13c8efe
commit f23adee
Showing
9 changed files
with
426 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# eslint-plugin-agoric | ||
|
||
Agoric-specific plugin | ||
|
||
## Installation | ||
|
||
You'll first need to install [ESLint](http://eslint.org): | ||
|
||
``` | ||
$ npm i eslint --save-dev | ||
``` | ||
|
||
Next, install `eslint-plugin-agoric`: | ||
|
||
``` | ||
$ npm install eslint-plugin-agoric --save-dev | ||
``` | ||
|
||
**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-agoric` globally. | ||
|
||
## Usage | ||
|
||
Add `agoric` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: | ||
|
||
```json | ||
{ | ||
"plugins": [ | ||
"agoric" | ||
] | ||
} | ||
``` | ||
|
||
|
||
Then configure the rules you want to use under the rules section. | ||
|
||
```json | ||
{ | ||
"rules": { | ||
"agoric/rule-name": 2 | ||
} | ||
} | ||
``` | ||
|
||
## Supported Rules | ||
|
||
* Fill in provided rules here | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
plugins: ["@agoric"], | ||
rules: { | ||
"@agoric/assert-fail-as-throw": 2 | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @fileoverview Agoric-specific plugin | ||
* @author Agoric | ||
*/ | ||
"use strict"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
|
||
var requireIndex = require("requireindex"); | ||
|
||
//------------------------------------------------------------------------------ | ||
// Plugin Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
|
||
// import all rules in lib/rules | ||
module.exports.rules = requireIndex(__dirname + "/rules"); | ||
module.exports.configs = requireIndex(__dirname + "/configs"); |
167 changes: 167 additions & 0 deletions
167
packages/eslint-plugin/lib/rules/assert-fail-as-throw.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/** | ||
* @author Toru Nagashima | ||
* See LICENSE file in root directory for full license. | ||
* | ||
* Modified by Michael FIG <mfig@agoric.com> for Agoric's assert.fail. | ||
* (This file used to be github.com/mysticatea/eslint-plugin-node/.../process-exit-as-throw.js.) | ||
*/ | ||
"use strict" | ||
|
||
const CodePathAnalyzer = safeRequire( | ||
"eslint/lib/linter/code-path-analysis/code-path-analyzer", | ||
"eslint/lib/code-path-analysis/code-path-analyzer" | ||
) | ||
const CodePathSegment = safeRequire( | ||
"eslint/lib/linter/code-path-analysis/code-path-segment", | ||
"eslint/lib/code-path-analysis/code-path-segment" | ||
) | ||
const CodePath = safeRequire( | ||
"eslint/lib/linter/code-path-analysis/code-path", | ||
"eslint/lib/code-path-analysis/code-path" | ||
) | ||
|
||
const originalLeaveNode = | ||
CodePathAnalyzer && CodePathAnalyzer.prototype.leaveNode | ||
|
||
/** | ||
* Imports a specific module. | ||
* @param {...string} moduleNames - module names to import. | ||
* @returns {object|null} The imported object, or null. | ||
*/ | ||
function safeRequire(...moduleNames) { | ||
for (const moduleName of moduleNames) { | ||
try { | ||
return require(moduleName) | ||
} catch (_err) { | ||
// Ignore. | ||
} | ||
} | ||
return null | ||
} | ||
|
||
/* istanbul ignore next */ | ||
/** | ||
* Copied from https://github.com/eslint/eslint/blob/16fad5880bb70e9dddbeab8ed0f425ae51f5841f/lib/code-path-analysis/code-path-analyzer.js#L137 | ||
* | ||
* @param {CodePathAnalyzer} analyzer - The instance. | ||
* @param {ASTNode} node - The current AST node. | ||
* @returns {void} | ||
*/ | ||
function forwardCurrentToHead(analyzer, node) { | ||
const codePath = analyzer.codePath | ||
const state = CodePath.getState(codePath) | ||
const currentSegments = state.currentSegments | ||
const headSegments = state.headSegments | ||
const end = Math.max(currentSegments.length, headSegments.length) | ||
let i = 0 | ||
let currentSegment = null | ||
let headSegment = null | ||
|
||
// Fires leaving events. | ||
for (i = 0; i < end; ++i) { | ||
currentSegment = currentSegments[i] | ||
headSegment = headSegments[i] | ||
|
||
if (currentSegment !== headSegment && currentSegment) { | ||
if (currentSegment.reachable) { | ||
analyzer.emitter.emit( | ||
"onCodePathSegmentEnd", | ||
currentSegment, | ||
node | ||
) | ||
} | ||
} | ||
} | ||
|
||
// Update state. | ||
state.currentSegments = headSegments | ||
|
||
// Fires entering events. | ||
for (i = 0; i < end; ++i) { | ||
currentSegment = currentSegments[i] | ||
headSegment = headSegments[i] | ||
|
||
if (currentSegment !== headSegment && headSegment) { | ||
CodePathSegment.markUsed(headSegment) | ||
if (headSegment.reachable) { | ||
analyzer.emitter.emit( | ||
"onCodePathSegmentStart", | ||
headSegment, | ||
node | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Checks whether a given node is `assert.fail()` or not. | ||
* | ||
* @param {ASTNode} node - A node to check. | ||
* @returns {boolean} `true` if the node is `assert.fail()`. | ||
*/ | ||
function isAssertFail(node) { | ||
return ( | ||
node.type === "CallExpression" && | ||
node.callee.type === "MemberExpression" && | ||
node.callee.computed === false && | ||
node.callee.object.type === "Identifier" && | ||
node.callee.object.name === "assert" && | ||
node.callee.property.type === "Identifier" && | ||
node.callee.property.name === "fail" | ||
) | ||
} | ||
|
||
/** | ||
* The function to override `CodePathAnalyzer.prototype.leaveNode` in order to | ||
* address `assert.fail()` as throw. | ||
* | ||
* @this CodePathAnalyzer | ||
* @param {ASTNode} node - A node to be left. | ||
* @returns {void} | ||
*/ | ||
function overrideLeaveNode(node) { | ||
if (isAssertFail(node)) { | ||
this.currentNode = node | ||
|
||
forwardCurrentToHead(this, node) | ||
CodePath.getState(this.codePath).makeThrow() | ||
|
||
this.original.leaveNode(node) | ||
this.currentNode = null | ||
} else { | ||
originalLeaveNode.call(this, node) | ||
} | ||
} | ||
|
||
const visitor = | ||
CodePathAnalyzer == null | ||
? {} | ||
: { | ||
Program: function installAssertFailAsThrow() { | ||
CodePathAnalyzer.prototype.leaveNode = overrideLeaveNode | ||
}, | ||
"Program:exit": function restoreAssertFailAsThrow() { | ||
CodePathAnalyzer.prototype.leaveNode = originalLeaveNode | ||
}, | ||
} | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: | ||
"make `assert.fail()` expressions the same code path as `throw`", | ||
category: "Possible Errors", | ||
recommended: true, | ||
url: | ||
"https://github.com/Agoric/agoric-sdk/blob/master/packages/eslint-plugin/lib/rules/assert-fail-as-throw.js", | ||
}, | ||
type: "problem", | ||
fixable: null, | ||
schema: [], | ||
supported: true || CodePathAnalyzer != null, | ||
}, | ||
create() { | ||
return visitor | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "@agoric/eslint-plugin", | ||
"version": "0.0.0", | ||
"description": "Agoric-specific ESLint plugin", | ||
"keywords": [ | ||
"eslint", | ||
"eslintplugin", | ||
"eslint-plugin" | ||
], | ||
"author": "Agoric", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"test": "exit 0", | ||
"build": "exit 0", | ||
"lint-fix": "exit 0", | ||
"lint-check": "exit 0" | ||
}, | ||
"dependencies": { | ||
"requireindex": "~1.1.0" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.8.0", | ||
"mocha": "^3.1.2" | ||
}, | ||
"engines": { | ||
"node": ">=0.10.0" | ||
}, | ||
"prettier": { | ||
"trailingComma": "all", | ||
"singleQuote": true | ||
}, | ||
"license": "Apache-2.0" | ||
} |
Oops, something went wrong.