diff --git a/.vscode/settings.json b/.vscode/settings.json index 8fca2c122..6574ea6f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ { "editor.insertSpaces": true, "editor.tabSize": 4, + "tslint.nodePath": "./node_modules/tslint/bin/tslint", "typescript.tsdk": "./node_modules/typescript/lib", "files.trimTrailingWhitespace": true } diff --git a/Gruntfile.js b/Gruntfile.js index d3994ba89..8b6f9f5f3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -200,29 +200,19 @@ module.exports = function(grunt) { ts: { default: { - src: [ - './src/**/*.ts{,x}' - ], - outDir: 'dist/src', - options: { - module: 'commonjs', - target: 'es5', - declaration: true, - failOnTypeErrors: true, - jsx: 'react' + tsconfig: { + tsconfig: './tsconfig.json', + passThrough: true, + updateFiles: true, + overwriteFiles: true, } }, 'test-data': { - src: [ - './test-data/**/*.ts{,x}' - ], - outDir: 'dist/test-data', - options: { - module: 'commonjs', - target: 'es5', - declaration: false, - failOnTypeErrors: true, - jsx: 'react' + tsconfig: { + tsconfig: './tsconfig.testdata.json', + passThrough: true, + updateFiles: true, + overwriteFiles: true } } }, @@ -248,6 +238,7 @@ module.exports = function(grunt) { let tslintJson = grunt.file.readJSON("tslint.json", { encoding: 'UTF-8' }); tslintJson.rules['no-multiline-string'] = false; tslintJson.rules['quotemark'] = false; + tslintJson.rules['object-literal-key-quotes'] = false; tslintJson.rules['max-func-body-length'] = false; return tslintJson; })() @@ -321,26 +312,28 @@ module.exports = function(grunt) { var tslintConfig = grunt.file.readJSON('tslint.json', { encoding: 'UTF-8' }); var rulesToSkip = { - 'no-unused-variable': true, - 'no-unexternalized-strings': true, - 'object-literal-key-quotes': true, - 'no-relative-imports': true, - 'no-empty-line-after-opening-brace': true, - 'no-duplicate-key': true, - 'align': true, - 'comment-format': true, - 'interface-name': true, - 'max-file-line-count': true, - 'member-ordering': true, - 'no-inferrable-types': true, - 'only-arrow-functions': true, - 'ordered-imports': true, - 'typedef-whitespace': true, - 'array-type': true, - 'completed-docs': true, - 'cyclomatic-complexity': true, - 'file-header': true, - 'max-classes-per-file': true + 'ban-types': true, + 'match-default-export-name': true, // requires type checking + 'newline-before-return': true, // kind of a silly rule + 'no-non-null-assertion': true, // in fact we prefer the opposite rule + 'prefer-template': true, // rule does not handle multi-line strings nicely + 'return-undefined': true, // requires type checking + 'no-unused-variable': true, // requires type checking + 'no-unexternalized-strings': true, // this is a VS Code specific rule + 'no-relative-imports': true, // this project uses relative imports + 'no-empty-line-after-opening-brace': true, // too strict + 'align': true, // no need + 'comment-format': true, // no need + 'interface-name': true, // no need + 'max-file-line-count': true, // no need + 'member-ordering': true, // too strict + 'no-inferrable-types': true, // we prefer the opposite + 'ordered-imports': true, // too difficult to turn on + 'typedef-whitespace': true, // too strict + 'completed-docs': true, // no need + 'cyclomatic-complexity': true, // too strict + 'file-header': true, // no need + 'max-classes-per-file': true // no need }; var errors = []; getAllRuleNames().forEach(function(ruleName) { diff --git a/README.md b/README.md index 0c6a4cce0..145cc421c 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,16 @@ tslint-microsoft-contrib A set of [TSLint](https://github.com/palantir/tslint) rules used on some Microsoft projects. -Version 4.0.1 (Stable) +Version 5.0.1 (Stable) ------------- The project has been in use for several years on multiple projects. Please report any bugs or false positives you might find! See our [Release Notes](https://github.com/Microsoft/tslint-microsoft-contrib/wiki/Release-Notes) to find the latest new rules. -Version 4.0.2 (In-Development) +Version 5.0.2 (In-Development) ------------- The [Latest Development Version](https://github.com/Microsoft/tslint-microsoft-contrib/tree/releases) is available online. +To use the nightly build set your npm version to `git://github.com/Microsoft/tslint-microsoft-contrib.git#releases` Installation ------------ @@ -25,18 +26,19 @@ Installation Alternately, you can download the files directly from GitHub: -* [4.0.0](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-4.0.0) -* [2.0.14](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-2.0.14) +* [5.0.1](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-5.0.1) +* [4.0.1](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-4.0.1) #### TSLint and corresponding tslint-microsoft-contrib version | TSLint version | tslint-microsoft-contrib version | | --- | --- | -| **>= 4.x** | 4.x | +| **>= 5.x** | 5.x (supporting TypeScript 2.3.x)| +| **>= 4.x** | 4.x (supporting TypeScript 2.1.x) | | **>= 3.2.x** | 2.x | -| **3.1.x** | unsupported | -| **3.0.x** | unsupported | -| **2.x** | 1.x | +| **3.1.x** | unsupported | +| **3.0.x** | unsupported | +| **2.x** | 1.x | Configuration ------------- @@ -61,7 +63,7 @@ The tslint.json file does not change format when using this package. Just add ou ##### Which Rules Should I Turn On? There certainly are a lot of options! Here are some links to get you started. -* Easiest Option - Our recommended ruleset is here: [recommended_ruleset.js](recommended_ruleset.js). You can also easily extend the ruleset by adding `"extends": "tslint-microsoft-contrib"` to your configuration. Please note, the default rules require the `--type-check` and `--project` TSLint options. +* Easiest Option - Our recommended ruleset is here: [recommended_ruleset.js](recommended_ruleset.js). You can also easily extend the ruleset by adding `"extends": "tslint-microsoft-contrib"` to your configuration. Please note, the default rules require the `--type-check` and `--project` TSLint options. Also, please note that adding a rule to the recommended ruleset is considered backwards compatible. If you reply on version ranges in your dependencies then you may find that new rules being added to the product create violations and fail your build. * A nice blog post on the MSDN secure development blog can be found here: [Automating Secure Development Lifecycle Checks in TypeScript with TSLint](https://blogs.msdn.microsoft.com/secdevblog/2016/05/11/automating-secure-development-lifecycle-checks-in-typescript-with-tslint/) * A wiki briefly describing the SDL and related rules is here: [TSLint and the Microsoft Security Development Lifecycle](https://github.com/Microsoft/tslint-microsoft-contrib/wiki/TSLint-and-the-Microsoft-Security-Development-Lifecycle) * And our configuration file with all options is available here: [tslint.json](tslint.json) @@ -114,7 +116,6 @@ Rule Name | Description | Since `no-relative-imports` | Do not use relative paths when importing external modules or ES6 import declarations. The advantages of removing all relative paths from imports is that 1) the import name will be consistent across all files and subdirectories so searching for usages is much easier. 2) Moving source files to different folders will not require you to edit your import statements. 3) It will be possible to copy and paste import lines between files regardless of the file location. And 4) version control diffs will be simplified by having overall fewer edits to the import lines.| 2.0.5 `no-reserved-keywords` | Do not use reserved keywords as names of local variables, fields, functions, or other identifiers. Since version 2.0.9 this rule accepts a parameter called allow-quoted-properties. If true, interface properties in quotes will be ignored. This can be a useful way to avoid verbose suppress-warning comments for generated d.ts files.| 0.0.1, 2.0.9 `no-single-line-block-comment` | Avoid single line block comments and use single line comments instead. Block comments do not nest properly and have no advantages over normal single-line comments| 2.0.10 -`no-sparse-arrays` | Do not use sparse arrays. Sparse arrays contain empty slots, most frequently due to multiple commas being used in an array literal. Based on the [ESLint no-sparse-arrays](http://eslint.org/docs/rules/no-sparse-arrays) rule | 1.0 `no-stateless-class` | A stateless class represents a failure in the object oriented design of the system. A class without state is better modeled as a module or given some state. A stateless class is defined as a class with only static members and no parent class.| 2.0.4 `no-string-based-set-immediate` | Do not use the version of setImmediate that accepts code as a string argument. However, it is acceptable to use the version of setImmediate where a direct reference to a function is provided as the callback argument | 0.0.1 `no-string-based-set-interval` | Do not use the version of setInterval that accepts code as a string argument. However, it is acceptable to use the version of setInterval where a direct reference to a function is provided as the callback argument | 0.0.1 @@ -128,9 +129,8 @@ Rule Name | Description | Since `no-unnecessary-override` | Do not write a method that only calls super() on the parent method with the same arguments. You can safely remove methods like this and Javascript will correctly dispatch the method to the parent object. | 2.0.4 `no-unnecessary-semicolons` | Remove unnecessary semicolons | 0.0.1 `no-unsupported-browser-code` | Avoid writing browser-specific code for unsupported browser versions. Browser versions are specified in the rule configuration options, eg: `[true, [ "IE 11", "Firefox > 40", "Chrome >= 45" ] ]`. Browser-specific blocks of code can then be designated with a single-line comment, like so: `// Browser specific: IE 10`, or with a jsdoc like this: `@browserspecific chrome 40`. | 2.0.10 -`no-unused-imports` | Deprecated - This rule is now covered by TSLint's no-unused-variables rule. However, it can still be useful to enable this rule and pair it with the fix-no-unused-imports formatter. | 0.0.1 `no-useless-files` | Avoid keeping files around that only contain commented out code, are completely empty, or only contain whitespace characters | 4.0.2 -`no-var-self` | Do not use `var self = this`; instead, manage scope with arrow functions/lambdas. Self variables are a common practice in JavaScript but can be avoided in TypeScript. By default the rule bans any assignments of the `this` reference. If you want to enforce a naming convention or allow some usages then configure the rule with a regex. By default the rule is configured with `(?!)` which matches nothing. You can pass `^self$` to allow variables named self or pass `^(?!self$)` to allow anything other than self, for example| 2.0.8 +`no-var-self` | Deprecated - This rule can be replaced with TSLint's no-this-assignment. Do not use `var self = this`; instead, manage scope with arrow functions/lambdas. Self variables are a common practice in JavaScript but can be avoided in TypeScript. By default the rule bans any assignments of the `this` reference. If you want to enforce a naming convention or allow some usages then configure the rule with a regex. By default the rule is configured with `(?!)` which matches nothing. You can pass `^self$` to allow variables named self or pass `^(?!self$)` to allow anything other than self, for example| 2.0.8 `no-with-statement` | Do not use with statements. Assign the item to a new variable instead | 0.0.1 `non-literal-require` | Detect `require()` function calls for something that is not a string literal. For security reasons, it is best to only require() string literals. Otherwise, it is perhaps possible for an attacker to somehow change the value and download arbitrary Javascript into your page. | 2.0.14 `possible-timing-attack` | Avoid timing attacks by not making direct string comparisons to sensitive data. Do not compare against variables named password, secret, api, apiKey, token, auth, pass, or hash. For more info see [Using Node.js Event Loop for Timing Attacks](https://snyk.io/blog/node-js-timing-attack-ccc-ctf/) | 2.0.11 @@ -158,7 +158,6 @@ Rule Name | Description | Since `react-tsx-curly-spacing` | Consistently use spaces around the brace characters of JSX attributes.You can either allow or bad spaces between the braces and the values they enclose.

One of the two following options are required:
* "always" enforces a space inside of curly braces (default)
* "never" disallows spaces inside of curly braces

By default, braces spanning multiple lines are not allowed with either setting. If you want to allow them you can specify an additional allowMultiline property with the value false.

Examples:
* "react-tsx-curly-spacing": [true, "always"]
* "react-tsx-curly-spacing": [true, "never"]
* "react-tsx-curly-spacing": [true, "never", {"allowMultiline": false}]

References
* [eslint-plugin-react jsx-curly-spacing rule](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md) | 2.0.14 `react-unused-props-and-state` | Remove unneeded properties defined in React Props and State interfaces. Any interface named Props or State is defined as a React interface. All fields in these interfaces must be referenced. This rule can be configured with regexes to match custom Props and State interface names.

Example for including all interfaces ending with Props or State:
*[ true, { 'props-interface-regex': 'Props$', 'state-interface-regex': 'State$' } ]* | 2.0.10 `underscore-consistent-invocation` | Enforce a consistent usage of the _ functions. By default, invoking underscore functions should begin with wrapping a variable in an underscore instance: `_(list).map(...)`. An alternative is to prefer using the static methods on the _ variable: `_.map(list, ...)`. The rule accepts single parameter called 'style' which can be the value 'static' or 'instance': `[true, { "style": "static" }]`| 2.0.10 -`use-isnan` | Deprecated - This rule is now part of the base TSLint product. Ensures that you use the isNaN() function to check for NaN references instead of a comparison to the NaN constant. Similar to the [use-isnan ESLint rule](http://eslint.org/docs/rules/use-isnan).| 1.0 `use-named-parameter` | Do not reference the arguments object by numerical index; instead, use a named parameter. This rule is similar to JSLint's [Use a named parameter](https://jslinterrors.com/use-a-named-parameter) rule. | 0.0.3 `valid-typeof` | Ensures that the results of typeof are compared against a valid string. This rule aims to prevent errors from likely typos by ensuring that when the result of a typeof operation is compared against a string, that the string is a valid value. Similar to the [valid-typeof ESLint rule](http://eslint.org/docs/rules/valid-typeof).| 1.0 @@ -170,7 +169,6 @@ These formatters assume that you use the UTF-8 file encoding. They may not work Formatter Name | Description | Since :---------- | :------------ | ------------- `fix-no-require-imports`| This formatter automatically converts imports from the require syntax to the ES6 syntax. For example `import Utils = require('Utils');` becomes `import {Utils} from 'Utils';`. However, be warned that the fix assumes that your imported module exports the correct thing. If anything goes wrong with your exports then you'll get a compiler failure saying there is no default export. | 2.0.8 -`fix-no-unused-imports` | This formatter automatically fixes any unused imports found by the no-unused-imports rule. | 2.0.8 `fix-no-var-keyword` | This formatter automatically converts var variable declarations into let variable declarations found by the no-var-keyword rule. | 2.0.8 Development diff --git a/additional_rule_metadata.json b/additional_rule_metadata.json index 0ed532fa2..272fb82e7 100644 --- a/additional_rule_metadata.json +++ b/additional_rule_metadata.json @@ -881,5 +881,102 @@ "issueClass": "Ignored", "issueType": "Warning", "group": "Ignored" + }, + "ban-types": { + "issueClass": "Ignored", + "issueType": "Error", + "severity": "Critical", + "level": "Opportunity for Excellence", + "group": "Configurable", + "commonWeaknessEnumeration": "710" + }, + "match-default-export-name": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "newline-before-return": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Low", + "level": "Opportunity for Excellence", + "group": "Whitespace", + "commonWeaknessEnumeration": "710" + }, + "no-duplicate-super": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-import-side-effect": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-invalid-template-strings": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Moderate", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-non-null-assertion": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-reference-import": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-sparse-arrays": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "no-unnecessary-callback-wrapper": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Correctness", + "commonWeaknessEnumeration": "710" + }, + "prefer-template": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "710" + }, + "return-undefined": { + "issueClass": "Non-SDL", + "issueType": "Warning", + "severity": "Important", + "level": "Opportunity for Excellence", + "group": "Clarity", + "commonWeaknessEnumeration": "710", + "recommendation": "false, // this actually affect the readability of the code" } } diff --git a/package.json b/package.json index 7b1b3c516..567f57aba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint-microsoft-contrib", - "version": "4.0.2", + "version": "5.0.2", "description": "TSLint Rules for Microsoft", "repository": { "type": "git", @@ -8,9 +8,18 @@ }, "author": "Hamlet D'Arcy", "contributors": [ - { "name": "Bernd Kiefer", "email": "bernd.kiefer@microsoft.com" }, - { "name": "Daniel Manesku", "email": "daniel.manesku@microsoft.com" }, - { "name": "Hamlet D'Arcy", "email": "hamlet.darcy@microsoft.com" } + { + "name": "Bernd Kiefer", + "email": "bernd.kiefer@microsoft.com" + }, + { + "name": "Daniel Manesku", + "email": "daniel.manesku@microsoft.com" + }, + { + "name": "Hamlet D'Arcy", + "email": "hamlet.darcy@microsoft.com" + } ], "bugs": { "url": "https://github.com/Microsoft/tslint-microsoft-contrib/issues" @@ -26,23 +35,26 @@ "scripts": { "test": "grunt all" }, + "dependencies": { + "tsutils": "^2.7.1" + }, "devDependencies": { "chai": "3.2.0", - "grunt": "0.4.5", - "grunt-contrib-clean": "0.6.0", - "grunt-contrib-copy": "0.8.0", - "grunt-contrib-watch": "0.6.1", - "grunt-mocha-test": "0.12.7", - "grunt-ts": "5.4.0", - "grunt-tslint": "4.0.1", + "grunt": "^1.0.1", + "grunt-contrib-clean": "^1.1.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-mocha-test": "0.13.2", + "grunt-ts": "^6.0.0-beta.16", + "grunt-tslint": "^5.0.1", "load-grunt-tasks": "3.2.0", "mocha": "2.2.5", "time-grunt": "1.2.1", - "tslint": "4.4.2", - "typescript": "2.1.4", + "tslint": "5.1.0", + "typescript": "2.4.2", "underscore": "1.8.3" }, "peerDependencies": { - "tslint": ">=4.0.0" + "tslint": ">=5.1.0" } } diff --git a/recommended_ruleset.js b/recommended_ruleset.js index 528410967..2d43d65fa 100644 --- a/recommended_ruleset.js +++ b/recommended_ruleset.js @@ -40,6 +40,7 @@ module.exports = { "forin": true, "jquery-deferred-must-complete": true, "label-position": true, + "match-default-export-name": true, "mocha-avoid-only": true, "mocha-no-side-effect-code": true, "no-any": true, @@ -52,27 +53,32 @@ module.exports = { "no-control-regex": true, "no-debugger": true, "no-duplicate-case": true, + "no-duplicate-super": true, "no-duplicate-variable": true, "no-empty": true, "no-floating-promises": true, "no-for-in-array": true, + "no-import-side-effect": true, "no-increment-decrement": true, "no-invalid-regexp": true, + "no-invalid-template-strings": true, "no-invalid-this": true, "no-jquery-raw-elements": true, "no-misused-new": true, + "no-non-null-assertion": true, + "no-reference-import": true, "no-regex-spaces": true, "no-sparse-arrays": true, "no-stateless-class": true, "no-string-literal": true, "no-string-throw": true, "no-unnecessary-bind": true, + "no-unnecessary-callback-wrapper": true, "no-unnecessary-initializer": true, "no-unnecessary-override": true, "no-unsafe-any": true, "no-unsafe-finally": true, "no-unused-expression": true, - "no-unused-new": true, "no-use-before-declare": true, "no-with-statement": true, "promise-function-async": true, @@ -146,6 +152,8 @@ module.exports = { "prefer-const": true, "prefer-for-of": true, "prefer-method-signature": true, + "prefer-template": true, + "return-undefined": false, // this actually affect the readability of the code "typedef": [true, "call-signature", "arrow-call-signature", "parameter", "arrow-parameter", "property-declaration", "variable-declaration", "member-variable-declaration"], "underscore-consistent-invocation": true, "unified-signatures": true, @@ -180,6 +188,7 @@ module.exports = { "import-spacing": true, "indent": [true, "spaces"], "linebreak-style": true, + "newline-before-return": true, "no-consecutive-blank-lines": true, "no-empty-line-after-opening-brace": false, "no-single-line-block-comment": true, @@ -201,6 +210,7 @@ module.exports = { * Controversial/Configurable rules. */ "ban": false, // only enable this if you have some code pattern that you want to ban + "ban-types": true, "cyclomatic-complexity": true, "file-header": false, // enable this rule only if you are legally required to add a file header "import-blacklist": false, // enable and configure this as you desire @@ -226,7 +236,6 @@ module.exports = { "no-missing-visibility-modifiers": false, // use tslint member-access rule instead "no-multiple-var-decl": false, // use tslint one-variable-per-declaration rule instead "no-switch-case-fall-through": false, // now supported by TypeScript compiler - "no-unused-imports": false, // use tslint no-unused-variable rule instead "typeof-compare": false, // the valid-typeof rule is currently superior to this version } }; diff --git a/src/chaiPreferContainsToIndexOfRule.ts b/src/chaiPreferContainsToIndexOfRule.ts index 4cdfe672d..b16d6e763 100644 --- a/src/chaiPreferContainsToIndexOfRule.ts +++ b/src/chaiPreferContainsToIndexOfRule.ts @@ -41,8 +41,7 @@ class ChaiPreferContainsToIndexOfRuleWalker extends ErrorTolerantWalker { if (node.expression.kind === ts.SyntaxKind.PropertyAccessExpression) { if (ChaiUtils.isEqualsInvocation(node.expression)) { if (this.isFirstArgumentNegative1(node)) { - this.addFailure( - this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING); } } } diff --git a/src/chaiVagueErrorsRule.ts b/src/chaiVagueErrorsRule.ts index 314738582..12e34ca47 100644 --- a/src/chaiVagueErrorsRule.ts +++ b/src/chaiVagueErrorsRule.ts @@ -41,7 +41,7 @@ class ChaiVagueErrorsRuleWalker extends ErrorTolerantWalker { protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { if (ChaiUtils.isExpectInvocation(node)) { if (/ok|true|false|undefined|null/.test(node.name.getText())) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING); } } super.visitPropertyAccessExpression(node); @@ -53,7 +53,7 @@ class ChaiVagueErrorsRuleWalker extends ErrorTolerantWalker { if (ChaiUtils.isEqualsInvocation(node.expression)) { if (node.arguments.length === 1) { if (/true|false|null|undefined/.test(node.arguments[0].getText())) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING); } } } @@ -68,9 +68,9 @@ class ChaiVagueErrorsRuleWalker extends ErrorTolerantWalker { || expectedValue.kind === ts.SyntaxKind.FalseKeyword; if (operator === '===' && expectingBooleanKeyword) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_TRUE)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_TRUE); } else if (operator === '!==' && expectingBooleanKeyword) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_FALSE)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPARE_FALSE); } } } diff --git a/src/exportNameRule.ts b/src/exportNameRule.ts index 26013784d..14ab19503 100644 --- a/src/exportNameRule.ts +++ b/src/exportNameRule.ts @@ -91,6 +91,7 @@ export class ExportNameWalker extends ErrorTolerantWalker { }); return exportStatements; } + return null; } private getExportStatements(element: ts.Statement): ts.Statement[] { @@ -127,8 +128,7 @@ export class ExportNameWalker extends ErrorTolerantWalker { if (!regex.test(this.getFilename())) { if (!this.isSuppressed(exportedName)) { const failureString: string = Rule.FAILURE_STRING + this.getSourceFile().fileName + ' and ' + exportedName; - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } } } diff --git a/src/fixNoUnusedImportsFormatter.ts b/src/fixNoUnusedImportsFormatter.ts deleted file mode 100644 index 58b538372..000000000 --- a/src/fixNoUnusedImportsFormatter.ts +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -import {RuleFailure, RuleFailurePosition} from 'tslint'; -import {BaseFormatter} from './utils/BaseFormatter'; - -/** - * Formatter that fixes your unused imports. - */ -/* tslint:disable:export-name */ -export class Formatter extends BaseFormatter { -/* tslint:enable:export-name */ - - constructor() { - super('no-unused-imports', function (this: Formatter, failure: RuleFailure): void { - const fileName: string = failure.getFileName(); - const start: RuleFailurePosition = failure.getStartPosition(); - const end: RuleFailurePosition = failure.getEndPosition(); - - let fileContents: string = this.readFile(fileName); - - let startOfViolation: number = fileContents.lastIndexOf('\n', start.getPosition()); - if (startOfViolation === -1) { - startOfViolation = 0; // make sure to handle the first line in the file - } - const endOfViolation: number = fileContents.indexOf('\n', end.getPosition()); - - let line: string = fileContents.substring(startOfViolation, endOfViolation); - line = line.trim(); // trim off any new lines, handle those in regular expression - line = line.replace(/\(/, '\\(').replace(/\)/, '\\)'); - const regex: RegExp = new RegExp('\\n*' + line + '\\n*', 'mg'); - - if (startOfViolation === 0) { - fileContents = fileContents.replace(regex, ''); - } else { - fileContents = fileContents.replace(regex, '\n'); - } - this.writeFile(fileName, fileContents); - /* tslint:disable:no-console */ - console.log('Automatically removing unused import. Please re-compile and re-lint: ' + fileName); - /* tslint:enable:no-console */ - }); - } -} \ No newline at end of file diff --git a/src/functionNameRule.ts b/src/functionNameRule.ts index a74738cd7..c8754f70c 100644 --- a/src/functionNameRule.ts +++ b/src/functionNameRule.ts @@ -61,22 +61,22 @@ class FunctionNameRuleWalker extends ErrorTolerantWalker { const name: string = node.name.getText(); if (AstUtils.isPrivate(node)) { if (!this.privateMethodRegex.test(name)) { - this.addFailure(this.createFailure(node.name.getStart(), node.name.getWidth(), - `Private method name does not match ${this.privateMethodRegex}: ${name}`)); + this.addFailureAt(node.name.getStart(), node.name.getWidth(), + `Private method name does not match ${this.privateMethodRegex}: ${name}`); } } else if (AstUtils.isProtected(node)) { if (!this.protectedMethodRegex.test(name)) { - this.addFailure(this.createFailure(node.name.getStart(), node.name.getWidth(), - `Protected method name does not match ${this.protectedMethodRegex}: ${name}`)); + this.addFailureAt(node.name.getStart(), node.name.getWidth(), + `Protected method name does not match ${this.protectedMethodRegex}: ${name}`); } } else if (AstUtils.isStatic(node)) { if (!this.staticMethodRegex.test(name)) { - this.addFailure(this.createFailure(node.name.getStart(), node.name.getWidth(), - `Static method name does not match ${this.staticMethodRegex}: ${name}`)); + this.addFailureAt(node.name.getStart(), node.name.getWidth(), + `Static method name does not match ${this.staticMethodRegex}: ${name}`); } } else if (!this.methodRegex.test(name)) { - this.addFailure(this.createFailure(node.name.getStart(), node.name.getWidth(), - `Method name does not match ${this.methodRegex}: ${name}`)); + this.addFailureAt(node.name.getStart(), node.name.getWidth(), + `Method name does not match ${this.methodRegex}: ${name}`); } super.visitMethodDeclaration(node); } @@ -85,8 +85,8 @@ class FunctionNameRuleWalker extends ErrorTolerantWalker { if (node.name != null) { const name: string = node.name.text; if (!this.functionRegex.test(name)) { - this.addFailure(this.createFailure(node.name.getStart(), node.name.getWidth(), - `Function name does not match ${this.functionRegex}: ${name}`)); + this.addFailureAt(node.name.getStart(), node.name.getWidth(), + `Function name does not match ${this.functionRegex}: ${name}`); } } super.visitFunctionDeclaration(node); diff --git a/src/importNameRule.ts b/src/importNameRule.ts index f54a3c827..5b5c0785e 100644 --- a/src/importNameRule.ts +++ b/src/importNameRule.ts @@ -14,6 +14,7 @@ export class Rule extends Lint.Rules.AbstractRule { ruleName: 'import-name', type: 'maintainability', description: 'The name of the imported module must match the name of the thing being imported', + hasFix: true, options: null, optionsDescription: '', typescriptOnly: true, @@ -82,17 +83,22 @@ class ImportNameRuleWalker extends ErrorTolerantWalker { super.visitImportDeclaration(node); } - private validateImport(node: ts.Node, importedName: string, moduleName: string): void { + private validateImport(node: ts.ImportEqualsDeclaration | ts.ImportDeclaration, importedName: string, moduleName: string): void { moduleName = moduleName.replace(/.*\//, ''); // chop off the path moduleName = this.makeCamelCase(moduleName); if (this.isImportNameValid(importedName, moduleName) === false) { const message: string = `Misnamed import. Import should be named '${moduleName}' but found '${importedName}'`; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + const nameNode = node.kind === ts.SyntaxKind.ImportEqualsDeclaration ? + (node).name : (node).importClause.name; + const nameNodeStartPos = nameNode.getStart(); + const fix = new Lint.Replacement(nameNodeStartPos, nameNode.end - nameNodeStartPos, moduleName); + this.addFailureAt(node.getStart(), node.getWidth(), message, fix); } } private makeCamelCase(input: string): string { - return input.replace(/[-|\.](.)/g, (match: string, group1: string): string => { + // tslint:disable-next-line:variable-name + return input.replace(/[-|\.](.)/g, (_match: string, group1: string): string => { return group1.toUpperCase(); }); } diff --git a/src/insecureRandomRule.ts b/src/insecureRandomRule.ts index e79896682..32ad25a16 100644 --- a/src/insecureRandomRule.ts +++ b/src/insecureRandomRule.ts @@ -39,9 +39,9 @@ class InsecureRandomRuleWalker extends ErrorTolerantWalker { protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { if (node.expression.getText() === 'Math' && node.name.text === 'random') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), MATH_FAIL_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), MATH_FAIL_STRING); } else if (node.name.text === 'pseudoRandomBytes') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), NODE_FAIL_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), NODE_FAIL_STRING); } super.visitPropertyAccessExpression(node); } diff --git a/src/jqueryDeferredMustCompleteRule.ts b/src/jqueryDeferredMustCompleteRule.ts index 97dc7abfa..3d36e31ed 100644 --- a/src/jqueryDeferredMustCompleteRule.ts +++ b/src/jqueryDeferredMustCompleteRule.ts @@ -80,8 +80,7 @@ class JQueryDeferredAnalyzer extends ErrorTolerantWalker { blockAnalyzer.visitNode(parent); if (!blockAnalyzer.isAlwaysCompleted()) { const failureString = Rule.FAILURE_STRING + '\'' + rootNode.getText() + '\''; - const failure = this.createFailure(rootNode.getStart(), rootNode.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(rootNode.getStart(), rootNode.getWidth(), failureString); } } diff --git a/src/maxFuncBodyLengthRule.ts b/src/maxFuncBodyLengthRule.ts index 492278fd9..2270153d8 100644 --- a/src/maxFuncBodyLengthRule.ts +++ b/src/maxFuncBodyLengthRule.ts @@ -3,6 +3,7 @@ import * as Lint from 'tslint'; import {AstUtils} from './utils/AstUtils'; import {Utils} from './utils/Utils'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; +import {forEachTokenWithTrivia} from 'tsutils'; /** * Implementation of the max-func-body-length rule. @@ -133,10 +134,9 @@ class MaxFunctionBodyLengthRuleWalker extends Lint.RuleWalker { }) .length; - const scanner = ts.createScanner(ts.ScriptTarget.ES5, false, ts.LanguageVariant.Standard, node.getText()); - Lint.scanAllTokens(scanner, (scanner: ts.Scanner) => { - if (scanner.getToken() === ts.SyntaxKind.MultiLineCommentTrivia) { - commentLineCount += scanner.getTokenText().split(/\n/).length; + forEachTokenWithTrivia(node, (text, tokenSyntaxKind) => { + if (tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia) { + commentLineCount += text.split(/\n/).length; } }); @@ -170,8 +170,7 @@ class MaxFunctionBodyLengthRuleWalker extends Lint.RuleWalker { } private addFuncBodyTooLongFailure(node: ts.FunctionLikeDeclaration, length: number) { - const failure = this.createFailure(node.getStart(), node.getWidth(), this.formatFailureText(node, length)); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), this.formatFailureText(node, length)); } private formatFailureText (node: ts.FunctionLikeDeclaration, length: number) { diff --git a/src/missingJsdocRule.ts b/src/missingJsdocRule.ts index 8841a451f..1d61cec71 100644 --- a/src/missingJsdocRule.ts +++ b/src/missingJsdocRule.ts @@ -35,8 +35,7 @@ class MissingJSDocWalker extends ErrorTolerantWalker { protected visitSourceFile(node: ts.SourceFile): void { if (!/^\/\*\*\s*$/gm.test(node.getFullText())) { const failureString = Rule.FAILURE_STRING + this.getSourceFile().fileName; - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } // do not continue walking } diff --git a/src/missingOptionalAnnotationRule.ts b/src/missingOptionalAnnotationRule.ts index d31f7c8ea..ee9989ef0 100644 --- a/src/missingOptionalAnnotationRule.ts +++ b/src/missingOptionalAnnotationRule.ts @@ -68,7 +68,7 @@ class MissingOptionalAnnotationWalker extends ErrorTolerantWalker { } else if (optionalParameterFound && parameter.initializer == null) { // we found a non-optional parameter that comes *after* an optional parameter const msg = Rule.FAILURE_STRING + parameter.getFullText(); - this.addFailure(this.createFailure(parameter.name.getStart(), parameter.name.getWidth(), msg)); + this.addFailureAt(parameter.name.getStart(), parameter.name.getWidth(), msg); } }); } diff --git a/src/mochaAvoidOnlyRule.ts b/src/mochaAvoidOnlyRule.ts index adbce1b37..97427746e 100644 --- a/src/mochaAvoidOnlyRule.ts +++ b/src/mochaAvoidOnlyRule.ts @@ -50,13 +50,13 @@ class MochaAvoidOnlyRuleWalker extends ErrorTolerantWalker { if (node.arguments[1].kind === ts.SyntaxKind.FunctionExpression || node.arguments[1].kind === ts.SyntaxKind.ArrowFunction) { if (node.expression.getText() === 'it.only') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_IT)); + this.addFailureAt(node.getStart(), node.expression.getText().length, Rule.FAILURE_STRING_IT); } else if (node.expression.getText() === 'specify.only') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_SPECIFY)); + this.addFailureAt(node.getStart(), node.expression.getText().length, Rule.FAILURE_STRING_SPECIFY); } else if (node.expression.getText() === 'describe.only') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_DESCRIBE)); + this.addFailureAt(node.getStart(), node.expression.getText().length, Rule.FAILURE_STRING_DESCRIBE); } else if (node.expression.getText() === 'context.only') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_CONTEXT)); + this.addFailureAt(node.getStart(), node.expression.getText().length, Rule.FAILURE_STRING_CONTEXT); } } } diff --git a/src/mochaNoSideEffectCodeRule.ts b/src/mochaNoSideEffectCodeRule.ts index 1aaeb3535..d235b61af 100644 --- a/src/mochaNoSideEffectCodeRule.ts +++ b/src/mochaNoSideEffectCodeRule.ts @@ -80,11 +80,11 @@ class MochaNoSideEffectCodeRuleWalker extends ErrorTolerantWalker { } } - protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void { + protected visitFunctionDeclaration(): void { // never walk into function declarations. new scopes are inherently safe } - protected visitClassDeclaration(node: ts.ClassDeclaration): void { + protected visitClassDeclaration(): void { // never walk into class declarations. new scopes are inherently safe } @@ -180,6 +180,6 @@ class MochaNoSideEffectCodeRuleWalker extends ErrorTolerantWalker { } //console.log(ts.SyntaxKind[initializer.kind] + ' ' + initializer.getText()); const message: string = FAILURE_STRING + Utils.trimTo(parentNode.getText(), 30); - this.addFailure(this.createFailure(parentNode.getStart(), parentNode.getWidth(), message)); + this.addFailureAt(parentNode.getStart(), parentNode.getWidth(), message); } } diff --git a/src/mochaUnneededDoneRule.ts b/src/mochaUnneededDoneRule.ts index 6857628be..d436b1baa 100644 --- a/src/mochaUnneededDoneRule.ts +++ b/src/mochaUnneededDoneRule.ts @@ -64,15 +64,13 @@ class MochaUnneededDoneRuleWalker extends ErrorTolerantWalker { ); const count: number = walker.getReferenceCount(node.body); if (count === 1) { - this.addFailure( - this.createFailure(doneIdentifier.getStart(), doneIdentifier.getWidth(), FAILURE_STRING + doneIdentifier.getText()) - ); + this.addFailureAt(doneIdentifier.getStart(), doneIdentifier.getWidth(), FAILURE_STRING + doneIdentifier.getText()); } } private isIdentifierInvokedDirectlyInBody(doneIdentifier: ts.Identifier, node: ts.FunctionLikeDeclaration): boolean { if (node.body == null || node.body.kind !== ts.SyntaxKind.Block) { - return; + return false; } const block: ts.Block = node.body; return Utils.exists(block.statements, (statement: ts.Statement): boolean => { diff --git a/src/noBackboneGetSetOutsideModelRule.ts b/src/noBackboneGetSetOutsideModelRule.ts index 9bc38e068..628e42c01 100644 --- a/src/noBackboneGetSetOutsideModelRule.ts +++ b/src/noBackboneGetSetOutsideModelRule.ts @@ -42,11 +42,11 @@ class NoBackboneGetSetOutsideModelRuleWalker extends ErrorTolerantWalker { const functionName: string = AstUtils.getFunctionName(node); if (functionName === 'get' && node.arguments.length === 1 && node.arguments[0].kind === ts.SyntaxKind.StringLiteral) { const msg: string = Rule.GET_FAILURE_STRING + node.getText(); - this.addFailure(this.createFailure(node.getStart(), node.getEnd(), msg)); + this.addFailureAt(node.getStart(), node.getEnd(), msg); } if (functionName === 'set' && node.arguments.length === 2 && node.arguments[0].kind === ts.SyntaxKind.StringLiteral) { const msg: string = Rule.SET_FAILURE_STRING + node.getText(); - this.addFailure(this.createFailure(node.getStart(), node.getEnd(), msg)); + this.addFailureAt(node.getStart(), node.getEnd(), msg); } } super.visitCallExpression(node); diff --git a/src/noConstantConditionRule.ts b/src/noConstantConditionRule.ts index 7b2ee4568..d8881627e 100644 --- a/src/noConstantConditionRule.ts +++ b/src/noConstantConditionRule.ts @@ -56,7 +56,7 @@ class NoConstantConditionRuleWalker extends ErrorTolerantWalker { protected visitIfStatement(node: ts.IfStatement): void { if (AstUtils.isConstantExpression(node.expression)) { const message: string = Rule.FAILURE_STRING + 'if (' + node.expression.getText() + ')'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } super.visitIfStatement(node); } @@ -64,7 +64,7 @@ class NoConstantConditionRuleWalker extends ErrorTolerantWalker { protected visitConditionalExpression(node: ts.ConditionalExpression): void { if (AstUtils.isConstantExpression(node.condition)) { const message: string = Rule.FAILURE_STRING + node.condition.getText() + ' ?'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } super.visitConditionalExpression(node); } @@ -73,7 +73,7 @@ class NoConstantConditionRuleWalker extends ErrorTolerantWalker { if (this.checkLoops) { if (AstUtils.isConstantExpression(node.expression)) { const message: string = Rule.FAILURE_STRING + 'while (' + node.expression.getText() + ')'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } } super.visitWhileStatement(node); @@ -83,7 +83,7 @@ class NoConstantConditionRuleWalker extends ErrorTolerantWalker { if (this.checkLoops) { if (AstUtils.isConstantExpression(node.expression)) { const message: string = Rule.FAILURE_STRING + 'while (' + node.expression.getText() + ')'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } } super.visitDoStatement(node); @@ -93,7 +93,7 @@ class NoConstantConditionRuleWalker extends ErrorTolerantWalker { if (this.checkLoops && node.condition != null) { if (AstUtils.isConstantExpression(node.condition)) { const message: string = Rule.FAILURE_STRING + ';' + node.condition.getText() + ';'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } } super.visitForStatement(node); diff --git a/src/noControlRegexRule.ts b/src/noControlRegexRule.ts index 9409d1b28..fea6629eb 100644 --- a/src/noControlRegexRule.ts +++ b/src/noControlRegexRule.ts @@ -44,7 +44,7 @@ class NoControlRegexRuleWalker extends ErrorTolerantWalker { protected visitRegularExpressionLiteral(node: ts.Node): void { if (/(\\x[0-1][0-9a-f])/.test(node.getText())) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } super.visitRegularExpressionLiteral(node); } @@ -57,7 +57,7 @@ class NoControlRegexRuleWalker extends ErrorTolerantWalker { if (arg1.kind === ts.SyntaxKind.StringLiteral) { const regexpText: string = (arg1).text; if (/[\x00-\x1f]/.test(regexpText)) { - this.addFailure(this.createFailure(arg1.getStart(), arg1.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(arg1.getStart(), arg1.getWidth(), Rule.FAILURE_STRING); } } } diff --git a/src/noCookiesRule.ts b/src/noCookiesRule.ts index bd6176833..edea1c011 100644 --- a/src/noCookiesRule.ts +++ b/src/noCookiesRule.ts @@ -7,7 +7,7 @@ import {ExtendedMetadata} from './utils/ExtendedMetadata'; /** * Implementation of the no-cookies-rule rule. */ -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Lint.Rules.TypedRule { public static metadata: ExtendedMetadata = { ruleName: 'no-cookies', @@ -26,22 +26,17 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING: string = 'Forbidden call to document.cookie'; - public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService : ts.LanguageService = ts.createLanguageService(languageServiceHost, documentRegistry); - return this.applyWithWalker(new NoCookiesWalker(sourceFile, this.getOptions(), languageService)); + public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + return this.applyWithWalker(new NoCookiesWalker(sourceFile, this.getOptions(), program)); } } class NoCookiesWalker extends ErrorTolerantWalker { - private languageService : ts.LanguageService; private typeChecker : ts.TypeChecker; - constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, languageService : ts.LanguageService) { + constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, program: ts.Program) { super(sourceFile, options); - this.languageService = languageService; - this.typeChecker = languageService.getProgram().getTypeChecker(); + this.typeChecker = program.getTypeChecker(); } protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void { @@ -52,12 +47,12 @@ class NoCookiesWalker extends ErrorTolerantWalker { const leftSideType: ts.Type = this.typeChecker.getTypeAtLocation(leftSide); const typeAsString: string = this.typeChecker.typeToString(leftSideType); if (leftSideType.flags === ts.TypeFlags.Any || typeAsString === 'Document') { - this.addFailure(this.createFailure(leftSide.getStart(), leftSide.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(leftSide.getStart(), leftSide.getWidth(), Rule.FAILURE_STRING); } } catch (e) { // the error thrown seems like a tslint error if (leftSide.getFullText().trim() === 'document') { - this.addFailure(this.createFailure(leftSide.getStart(), leftSide.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(leftSide.getStart(), leftSide.getWidth(), Rule.FAILURE_STRING); } } } diff --git a/src/noDeleteExpressionRule.ts b/src/noDeleteExpressionRule.ts index 11186b0ca..389af5c3b 100644 --- a/src/noDeleteExpressionRule.ts +++ b/src/noDeleteExpressionRule.ts @@ -46,7 +46,7 @@ class NoDeleteExpression extends ErrorTolerantWalker { public addNoDeleteFailure(deletedObject: ts.Node): void { const msg: string = Rule.FAILURE_STRING + deletedObject.getFullText().trim(); - this.addFailure(this.createFailure(deletedObject.getStart(), deletedObject.getWidth(), msg)); + this.addFailureAt(deletedObject.getStart(), deletedObject.getWidth(), msg); } } diff --git a/src/noDisableAutoSanitizationRule.ts b/src/noDisableAutoSanitizationRule.ts index 065726585..65107a45c 100644 --- a/src/noDisableAutoSanitizationRule.ts +++ b/src/noDisableAutoSanitizationRule.ts @@ -36,7 +36,7 @@ class NoDisableAutoSanitizationWalker extends ErrorTolerantWalker { protected visitCallExpression(node: ts.CallExpression): void { const functionName : string = AstUtils.getFunctionName(node); if (functionName === 'execUnsafeLocalFunction' || functionName === 'setInnerHTMLUnsafe') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + functionName)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + functionName); } super.visitCallExpression(node); } diff --git a/src/noDocumentDomainRule.ts b/src/noDocumentDomainRule.ts index 68af044e4..af4a0bc64 100644 --- a/src/noDocumentDomainRule.ts +++ b/src/noDocumentDomainRule.ts @@ -37,7 +37,7 @@ class NoDocumentDomainRuleWalker extends ErrorTolerantWalker { && node.left.kind === ts.SyntaxKind.PropertyAccessExpression && this.isDocumentDomainProperty(node.left)) { const msg: string = Rule.FAILURE_STRING + node.getFullText().trim(); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), msg)); + this.addFailureAt(node.getStart(), node.getWidth(), msg); } super.visitBinaryExpression(node); } diff --git a/src/noDocumentWriteRule.ts b/src/noDocumentWriteRule.ts index ead592212..40b42bae1 100644 --- a/src/noDocumentWriteRule.ts +++ b/src/noDocumentWriteRule.ts @@ -42,9 +42,9 @@ class NoDocumentWriteWalker extends ErrorTolerantWalker { if (node.arguments.length === 1) { const functionName: string = AstUtils.getFunctionName(node); if (functionName === 'write') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.WRITE_FAILURE)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.WRITE_FAILURE); } else if (functionName === 'writeln') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.WRITELN_FAILURE)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.WRITELN_FAILURE); } } } diff --git a/src/noDuplicateCaseRule.ts b/src/noDuplicateCaseRule.ts index 54e07da99..e1f3e27da 100644 --- a/src/noDuplicateCaseRule.ts +++ b/src/noDuplicateCaseRule.ts @@ -41,7 +41,7 @@ class NoDuplicateCaseRuleWalker extends ErrorTolerantWalker { if (clause.expression != null) { const caseText = clause.expression.getText(); if (seenLabels.indexOf(caseText) > -1) { - this.addFailure(this.createFailure(clause.getStart(), clause.getWidth(), Rule.FAILURE_STRING + caseText)); + this.addFailureAt(clause.getStart(), clause.getWidth(), Rule.FAILURE_STRING + caseText); } else { seenLabels.push(caseText); } diff --git a/src/noDuplicateParameterNamesRule.ts b/src/noDuplicateParameterNamesRule.ts index 0440bc48a..0a1f12714 100644 --- a/src/noDuplicateParameterNamesRule.ts +++ b/src/noDuplicateParameterNamesRule.ts @@ -63,8 +63,8 @@ class NoDuplicateParameterNamesWalker extends ErrorTolerantWalker { const parameterName : string = (parameter.name).text; // how does one check if the union type is Identifier? if (parameterName != null) { if (seenNames[parameterName]) { - this.addFailure(this.createFailure( - parameter.name.getStart(), parameterName.length, Rule.FAILURE_STRING + '\'' + parameterName + '\'')); + this.addFailureAt( + parameter.name.getStart(), parameterName.length, Rule.FAILURE_STRING + '\'' + parameterName + '\''); } else { seenNames[parameterName] = true; } diff --git a/src/noEmptyInterfacesRule.ts b/src/noEmptyInterfacesRule.ts index a7d4c3c97..69074f79e 100644 --- a/src/noEmptyInterfacesRule.ts +++ b/src/noEmptyInterfacesRule.ts @@ -37,10 +37,8 @@ class NoEmptyInterfacesRuleWalker extends ErrorTolerantWalker { protected visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void { // do we have an empty interface? if (this.isInterfaceEmpty(node) && !this.hasMultipleParents(node)) { - this.addFailure( - this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), Rule.FAILURE_STRING + '\'' + node.name.getText() + '\'' - ) ); } super.visitInterfaceDeclaration(node); diff --git a/src/noEmptyLineAfterOpeningBraceRule.ts b/src/noEmptyLineAfterOpeningBraceRule.ts index e0e26bf82..1fc5a7b3d 100644 --- a/src/noEmptyLineAfterOpeningBraceRule.ts +++ b/src/noEmptyLineAfterOpeningBraceRule.ts @@ -3,6 +3,7 @@ import * as Lint from 'tslint'; import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; +import {forEachTokenWithTrivia} from 'tsutils'; /** * Implementation of the no-empty-line-after-opening-brace rule. @@ -50,19 +51,18 @@ class NoEmptyLineAfterOpeningBraceWalker extends ErrorTolerantWalker { let previous: ts.SyntaxKind; let previousPrevious: ts.SyntaxKind; - Lint.scanAllTokens(this.scanner, (scanner: ts.Scanner): void => { + forEachTokenWithTrivia(node, ({}, tokenSyntaxKind, range) => { if (previousPrevious === ts.SyntaxKind.OpenBraceToken && previous === ts.SyntaxKind.NewLineTrivia && - scanner.getToken() === ts.SyntaxKind.NewLineTrivia) { + tokenSyntaxKind === ts.SyntaxKind.NewLineTrivia) { - const leadingEmptyLineFailure = this.createFailure(scanner.getStartPos(), 1, Rule.FAILURE_STRING); - this.addFailure(leadingEmptyLineFailure); + this.addFailureAt(range.pos, 1, Rule.FAILURE_STRING); } //ignore empty spaces - if (scanner.getToken() !== ts.SyntaxKind.WhitespaceTrivia) { + if (tokenSyntaxKind !== ts.SyntaxKind.WhitespaceTrivia) { previousPrevious = previous; - previous = scanner.getToken(); + previous = tokenSyntaxKind; } }); } diff --git a/src/noExecScriptRule.ts b/src/noExecScriptRule.ts index e7498dfa4..44fd50477 100644 --- a/src/noExecScriptRule.ts +++ b/src/noExecScriptRule.ts @@ -44,7 +44,7 @@ class NoEvalScriptWalker extends ErrorTolerantWalker { const functionName : string = AstUtils.getFunctionName(node); if (functionName === 'execScript') { const msg : string = Rule.FAILURE_STRING + expression.getFullText().trim(); - this.addFailure(this.createFailure(expression.getStart(), expression.getWidth(), msg)); + this.addFailureAt(expression.getStart(), expression.getWidth(), msg); } } } diff --git a/src/noForInRule.ts b/src/noForInRule.ts index 8eca3e68d..fd680ea55 100644 --- a/src/noForInRule.ts +++ b/src/noForInRule.ts @@ -27,7 +27,7 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING_FACTORY(initializer: string, expression: string): string { //tslint:disable-next-line:max-line-length return `Do not use the 'for in' statement: 'for (${initializer} in ${expression})'. If this is an object, use 'Object.keys' instead. If this is an array use a standard 'for' loop instead.`; - }; + } public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new NoForInRuleWalker(sourceFile, this.getOptions())); @@ -40,6 +40,6 @@ class NoForInRuleWalker extends ErrorTolerantWalker { const expression: string = node.expression.getText(); const msg: string = Rule.FAILURE_STRING_FACTORY(initializer, expression); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), msg)); + this.addFailureAt(node.getStart(), node.getWidth(), msg); } } diff --git a/src/noFunctionConstructorWithStringArgsRule.ts b/src/noFunctionConstructorWithStringArgsRule.ts index 7832a6e19..54ef34b3b 100644 --- a/src/noFunctionConstructorWithStringArgsRule.ts +++ b/src/noFunctionConstructorWithStringArgsRule.ts @@ -28,28 +28,20 @@ export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING: string = 'forbidden: Function constructor with string arguments '; public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); - return this.applyWithWalker(new NoFunctionConstructorWithStringArgsWalker(sourceFile, this.getOptions(), languageService)); + return this.applyWithWalker(new NoFunctionConstructorWithStringArgsWalker(sourceFile, this.getOptions())); } } class NoFunctionConstructorWithStringArgsWalker extends ErrorTolerantWalker { - private languageService: ts.LanguageService; - private typeChecker : ts.TypeChecker; - - public constructor(sourceFile : ts.SourceFile, options : Lint.IOptions, languageServices : ts.LanguageService) { + public constructor(sourceFile : ts.SourceFile, options : Lint.IOptions) { super(sourceFile, options); - this.languageService = languageServices; - this.typeChecker = this.languageService.getProgram().getTypeChecker(); } protected visitNewExpression(node: ts.NewExpression): void { const functionName = AstUtils.getFunctionName(node); if (functionName === 'Function') { if (node.arguments.length > 0) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } } super.visitNewExpression(node); diff --git a/src/noFunctionExpressionRule.ts b/src/noFunctionExpressionRule.ts index f1efac204..c31736ff9 100644 --- a/src/noFunctionExpressionRule.ts +++ b/src/noFunctionExpressionRule.ts @@ -35,12 +35,12 @@ export class Rule extends Lint.Rules.AbstractRule { class NoFunctionExpressionRuleWalker extends ErrorTolerantWalker { protected visitFunctionExpression(node: ts.FunctionExpression): void { const walker = new SingleFunctionWalker(this.getSourceFile(), this.getOptions()); - node.getChildren().forEach((node: ts.Node) => { - walker.walk(node); + node.getChildren().forEach((child: ts.Node) => { + walker.walk(child); }); // function expression that access 'this' is allowed if (!walker.isAccessingThis) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } super.visitFunctionExpression(node); } @@ -55,10 +55,10 @@ class SingleFunctionWalker extends ErrorTolerantWalker { super.visitNode(node); } /* tslint:disable:no-empty */ - protected visitFunctionExpression(node: ts.FunctionExpression): void { + protected visitFunctionExpression(): void { // do not visit inner blocks } - protected visitArrowFunction(node: ts.ArrowFunction): void { + protected visitArrowFunction(): void { // do not visit inner blocks } /* tslint:enable:no-empty */ diff --git a/src/noHttpStringRule.ts b/src/noHttpStringRule.ts index d00c25887..61d4e929c 100644 --- a/src/noHttpStringRule.ts +++ b/src/noHttpStringRule.ts @@ -38,16 +38,28 @@ export class Rule extends Lint.Rules.AbstractRule { class NoHttpStringWalker extends ErrorTolerantWalker { protected visitStringLiteral(node: ts.StringLiteral): void { - const stringText : string = (node).text; + this.visitLiteralExpression(node); + super.visitStringLiteral(node); + } + + protected visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) { + this.visitLiteralExpression(node); + } else if (node.kind === ts.SyntaxKind.TemplateHead) { + this.visitLiteralExpression(node); + } + super.visitNode(node); + } + + private visitLiteralExpression(node: ts.LiteralExpression | ts.LiteralLikeNode): void { + const stringText : string = node.text; // tslint:disable no-http-string if (stringText.indexOf('http:') === 0) { if (!this.isSuppressed(stringText)) { const failureString = Rule.FAILURE_STRING + '\'' + stringText + '\''; - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } } - super.visitStringLiteral(node); } private isSuppressed(stringText: string) : boolean { diff --git a/src/noIncrementDecrementRule.ts b/src/noIncrementDecrementRule.ts index 451a3fe75..0c37162d9 100644 --- a/src/noIncrementDecrementRule.ts +++ b/src/noIncrementDecrementRule.ts @@ -42,9 +42,9 @@ class NoIncrementDecrementWalker extends ErrorTolerantWalker { private validateUnaryExpression(node : ts.PrefixUnaryExpression | ts.PostfixUnaryExpression) { if (node.operator === ts.SyntaxKind.PlusPlusToken) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), 'Forbidden ++ operator')); + this.addFailureAt(node.getStart(), node.getWidth(), 'Forbidden ++ operator'); } else if (node.operator === ts.SyntaxKind.MinusMinusToken) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), 'Forbidden -- operator')); + this.addFailureAt(node.getStart(), node.getWidth(), 'Forbidden -- operator'); } } diff --git a/src/noInnerHtmlRule.ts b/src/noInnerHtmlRule.ts index d32f18ada..99656f6bd 100644 --- a/src/noInnerHtmlRule.ts +++ b/src/noInnerHtmlRule.ts @@ -43,9 +43,9 @@ class NoInnerHtmlRuleWalker extends ErrorTolerantWalker { const propAccess: ts.PropertyAccessExpression = node.left; const propName: string = propAccess.name.text; if (propName === 'innerHTML') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_INNER + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_INNER + node.getText()); } else if (propName === 'outerHTML') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_OUTER + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_OUTER + node.getText()); } } } @@ -56,7 +56,7 @@ class NoInnerHtmlRuleWalker extends ErrorTolerantWalker { const functionName = AstUtils.getFunctionName(node); if (functionName === 'html') { if (node.arguments.length > 0) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_JQUERY + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_JQUERY + node.getText()); } } super.visitCallExpression(node); diff --git a/src/noInvalidRegexpRule.ts b/src/noInvalidRegexpRule.ts index f7ad61bed..7112f9dfe 100644 --- a/src/noInvalidRegexpRule.ts +++ b/src/noInvalidRegexpRule.ts @@ -46,11 +46,10 @@ class NoInvalidRegexpRuleWalker extends ErrorTolerantWalker { if (arg1.kind === ts.SyntaxKind.StringLiteral) { const regexpText: string = (arg1).text; try { - /* tslint:disable:no-unused-new */ + // tslint:disable-next-line:no-unused-expression new RegExp(regexpText); - /* tslint:enable:no-unused-new */ } catch (e) { - this.addFailure(this.createFailure(arg1.getStart(), arg1.getWidth(), e.message)); + this.addFailureAt(arg1.getStart(), arg1.getWidth(), e.message); } } } diff --git a/src/noJqueryRawElementsRule.ts b/src/noJqueryRawElementsRule.ts index 809b63bc0..f09e9c37e 100644 --- a/src/noJqueryRawElementsRule.ts +++ b/src/noJqueryRawElementsRule.ts @@ -39,13 +39,13 @@ class NoJqueryRawElementsRuleWalker extends Lint.RuleWalker { const firstArg: ts.Expression = node.arguments[0]; if (firstArg.kind === ts.SyntaxKind.StringLiteral) { if (this.isComplexHtmlElement(firstArg)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_COMPLEX + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_COMPLEX + node.getText()); } } else { const finder = new HtmlLikeStringLiteralFinder(this.getSourceFile(), this.getOptions()); finder.walk(node.arguments[0]); if (finder.isFound()) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_MANIPULATION + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_MANIPULATION + node.getText()); } } } diff --git a/src/noMissingVisibilityModifiersRule.ts b/src/noMissingVisibilityModifiersRule.ts index dfe4ee996..8402a6bb3 100644 --- a/src/noMissingVisibilityModifiersRule.ts +++ b/src/noMissingVisibilityModifiersRule.ts @@ -35,8 +35,7 @@ class MissingVisibilityModifierWalker extends ErrorTolerantWalker { protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void { if (this.isMissingVisibilityModifier(node)) { const failureString = 'Field missing visibility modifier: ' + this.getFailureCodeSnippet(node); - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } super.visitPropertyDeclaration(node); } @@ -44,8 +43,7 @@ class MissingVisibilityModifierWalker extends ErrorTolerantWalker { protected visitMethodDeclaration(node: ts.MethodDeclaration): void { if (this.isMissingVisibilityModifier(node)) { const failureString = 'Method missing visibility modifier: ' + this.getFailureCodeSnippet(node); - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } super.visitMethodDeclaration(node); } diff --git a/src/noMultilineStringRule.ts b/src/noMultilineStringRule.ts index d2c6058f5..588dfe9cd 100644 --- a/src/noMultilineStringRule.ts +++ b/src/noMultilineStringRule.ts @@ -38,7 +38,7 @@ class NoMultilineStringWalker extends ErrorTolerantWalker { const fullText : string = node.getFullText(); const firstLine : string = fullText.substring(0, fullText.indexOf('\n')); const trimmed : string = firstLine.substring(0, 40).trim(); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + trimmed + '...')); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + trimmed + '...'); } super.visitNode(node); } diff --git a/src/noMultipleVarDeclRule.ts b/src/noMultipleVarDeclRule.ts index a74c0bf8c..943e9162f 100644 --- a/src/noMultipleVarDeclRule.ts +++ b/src/noMultipleVarDeclRule.ts @@ -36,8 +36,8 @@ export class Rule extends Lint.Rules.AbstractRule { class NoMultipleVarDeclRuleWalker extends ErrorTolerantWalker { protected visitVariableStatement(node: ts.VariableStatement): void { if (node.declarationList.declarations.length > 1) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), - Rule.FAILURE_STRING + node.declarationList.declarations[0].getText() + ',')); + this.addFailureAt(node.getStart(), node.getWidth(), + Rule.FAILURE_STRING + node.declarationList.declarations[0].getText() + ','); } super.visitVariableStatement(node); } diff --git a/src/noOctalLiteralRule.ts b/src/noOctalLiteralRule.ts index 2cba73d57..12039f222 100644 --- a/src/noOctalLiteralRule.ts +++ b/src/noOctalLiteralRule.ts @@ -47,7 +47,7 @@ class NoOctalLiteral extends ErrorTolerantWalker { const startOfMatch = node.getStart() + node.getText().indexOf(octalValue); const width = octalValue.length; - this.addFailure(this.createFailure(startOfMatch, width, Rule.FAILURE_STRING + octalValue)); + this.addFailureAt(startOfMatch, width, Rule.FAILURE_STRING + octalValue); } } } diff --git a/src/noRegexSpacesRule.ts b/src/noRegexSpacesRule.ts index da84e679d..d821c4ef9 100644 --- a/src/noRegexSpacesRule.ts +++ b/src/noRegexSpacesRule.ts @@ -36,7 +36,7 @@ class NoRegexSpacesRuleWalker extends ErrorTolerantWalker { const match: RegExpExecArray = /( {2,})+?/.exec(node.getText()); if (match != null) { const replacement: string = '{' + match[0].length + '}'; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + replacement)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + replacement); } super.visitRegularExpressionLiteral(node); diff --git a/src/noRelativeImportsRule.ts b/src/noRelativeImportsRule.ts index 2475f8620..d09c74afc 100644 --- a/src/noRelativeImportsRule.ts +++ b/src/noRelativeImportsRule.ts @@ -37,12 +37,12 @@ class NoRelativeImportsRuleWalker extends ErrorTolerantWalker { if (node.kind === ts.SyntaxKind.ExternalModuleReference) { const moduleExpression: ts.Expression = (node).expression; if (!this.isModuleExpressionValid(moduleExpression)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_EXT + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_EXT + node.getText()); } } else if (node.kind === ts.SyntaxKind.ImportDeclaration) { const moduleExpression: ts.Expression = (node).moduleSpecifier; if (!this.isModuleExpressionValid(moduleExpression)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING_IMPORT + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING_IMPORT + node.getText()); } } super.visitNode(node); diff --git a/src/noSingleLineBlockCommentRule.ts b/src/noSingleLineBlockCommentRule.ts index 063289fb3..c997d2321 100644 --- a/src/noSingleLineBlockCommentRule.ts +++ b/src/noSingleLineBlockCommentRule.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import * as Lint from 'tslint'; -import {AstUtils} from './utils/AstUtils'; +import {forEachTokenWithTrivia} from 'tsutils'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; const FAILURE_STRING: string = 'Replace block comment with a single-line comment'; @@ -31,45 +31,19 @@ export class Rule extends Lint.Rules.AbstractRule { } } -class NoSingleLineBlockCommentRuleWalker extends Lint.SkippableTokenAwareRuleWalker { +class NoSingleLineBlockCommentRuleWalker extends Lint.RuleWalker { public visitSourceFile(node: ts.SourceFile) { - const scanner: ts.Scanner = ts.createScanner( - ts.ScriptTarget.ES5, - false, // do not skip comments - AstUtils.getLanguageVariant(node), - node.text - ); - Lint.scanAllTokens(scanner, (scanner: ts.Scanner): void => { - const startPos = scanner.getStartPos(); - if ((this).tokensToSkipStartEndMap[startPos] != null) { - // tokens to skip are places where the scanner gets confused about what the token is, without the proper context - // (specifically, regex, identifiers, and templates). So skip those tokens. - scanner.setTextPos((this).tokensToSkipStartEndMap[startPos]); - return; - } - - if (scanner.getToken() === ts.SyntaxKind.MultiLineCommentTrivia) { - const commentText: string = scanner.getTokenText(); - const startPosition: number = scanner.getTokenPos(); - - if (this.isSingleLineComment(commentText) - && this.isNextTokenOnANewLine(scanner) - && this.isTsLintSuppression(commentText) === false) { - this.addFailure(this.createFailure(startPosition, commentText.length, FAILURE_STRING)); - } + forEachTokenWithTrivia(node, (fullText, tokenSyntaxKind, range) => { + const tokenText = fullText.substring(range.pos, range.end); + if (tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia + && this.isSingleLineComment(tokenText) + && !this.isTsLintSuppression(tokenText)) { + this.addFailureAt(range.pos, range.end - range.pos, FAILURE_STRING); } }); } - private isNextTokenOnANewLine(scanner: ts.Scanner): boolean { - return scanner.lookAhead((): boolean => { - scanner.scan(); // scan the next token - return scanner.hasPrecedingLineBreak(); // if the token is preceded by line break then it was on a new line - }); - - } - private isSingleLineComment(commentText: string): boolean { const lines: string[] = commentText.split(/\r?\n/); return lines.length === 1; diff --git a/src/noSparseArraysRule.ts b/src/noSparseArraysRule.ts deleted file mode 100644 index 5b348169a..000000000 --- a/src/noSparseArraysRule.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as ts from 'typescript'; -import * as Lint from 'tslint'; - -import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; -import {Utils} from './utils/Utils'; -import {ExtendedMetadata} from './utils/ExtendedMetadata'; - -/** - * Implementation of the no-sparse-arrays rule. - */ -export class Rule extends Lint.Rules.AbstractRule { - - public static metadata: ExtendedMetadata = { - ruleName: 'no-sparse-arrays', - type: 'maintainability', - description: 'Do not use sparse arrays. Sparse arrays contain empty slots, most frequently due to multiple ' + - 'commas being used in an array literal.', - options: null, - optionsDescription: '', - typescriptOnly: true, - issueClass: 'Non-SDL', - issueType: 'Warning', - severity: 'Important', - level: 'Opportunity for Excellence', - group: 'Correctness', - commonWeaknessEnumeration: '398, 710' - }; - - public static FAILURE_STRING: string = 'Unexpected comma in middle of array'; - - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new NoSparseArraysRuleWalker(sourceFile, this.getOptions())); - } - -} - -class NoSparseArraysRuleWalker extends ErrorTolerantWalker { - protected visitNode(node: ts.Node): void { - if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) { - if (this.isSparseArray(node)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); - } - } - super.visitNode(node); - } - - private isSparseArray(node: ts.ArrayLiteralExpression): boolean { - return Utils.exists(node.elements, (element: ts.Node): boolean => { - return element.kind === ts.SyntaxKind.OmittedExpression; - }); - } -} diff --git a/src/noStatelessClassRule.ts b/src/noStatelessClassRule.ts index f2da04879..f15746aed 100644 --- a/src/noStatelessClassRule.ts +++ b/src/noStatelessClassRule.ts @@ -37,7 +37,7 @@ class NoStatelessClassRuleWalker extends ErrorTolerantWalker { protected visitClassDeclaration(node: ts.ClassDeclaration): void { if (!this.isClassStateful(node)) { const className: string = node.name == null ? '' : node.name.text; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + className)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + className); } super.visitClassDeclaration(node); } diff --git a/src/noStringBasedSetImmediateRule.ts b/src/noStringBasedSetImmediateRule.ts index 3e6e7389d..8018bec28 100644 --- a/src/noStringBasedSetImmediateRule.ts +++ b/src/noStringBasedSetImmediateRule.ts @@ -7,7 +7,7 @@ import {ExtendedMetadata} from './utils/ExtendedMetadata'; /** * Implementation of the no-string-parameter-to-function-call rule. */ -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Lint.Rules.OptionallyTypedRule { public static metadata: ExtendedMetadata = { ruleName: 'no-string-based-set-immediate', @@ -25,12 +25,12 @@ export class Rule extends Lint.Rules.AbstractRule { }; public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); + return this.applyWithProgram(sourceFile, undefined); + } + public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( - sourceFile , 'setImmediate', this.getOptions(), languageService + sourceFile , 'setImmediate', this.getOptions(), program ); return this.applyWithWalker(walker); } diff --git a/src/noStringBasedSetIntervalRule.ts b/src/noStringBasedSetIntervalRule.ts index 54159bff3..6f0596ab5 100644 --- a/src/noStringBasedSetIntervalRule.ts +++ b/src/noStringBasedSetIntervalRule.ts @@ -7,7 +7,7 @@ import {ExtendedMetadata} from './utils/ExtendedMetadata'; /** * Implementation of the no-string-based-set-interval rule. */ -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Lint.Rules.OptionallyTypedRule { public static metadata: ExtendedMetadata = { ruleName: 'no-string-based-set-interval', @@ -25,13 +25,14 @@ export class Rule extends Lint.Rules.AbstractRule { }; public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); + return this.applyWithProgram(sourceFile, undefined); + } + public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( - sourceFile , 'setInterval', this.getOptions(), languageService + sourceFile , 'setInterval', this.getOptions(), program ); + return this.applyWithWalker(walker); } } diff --git a/src/noStringBasedSetTimeoutRule.ts b/src/noStringBasedSetTimeoutRule.ts index 8416f5cf6..d07f1805b 100644 --- a/src/noStringBasedSetTimeoutRule.ts +++ b/src/noStringBasedSetTimeoutRule.ts @@ -7,7 +7,7 @@ import {ExtendedMetadata} from './utils/ExtendedMetadata'; /** * Implementation of the no-string-based-set-timeout rule. */ -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Lint.Rules.OptionallyTypedRule { public static metadata: ExtendedMetadata = { ruleName: 'no-string-based-set-timeout', @@ -25,12 +25,12 @@ export class Rule extends Lint.Rules.AbstractRule { }; public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); + return this.applyWithProgram(sourceFile, undefined); + } + public applyWithProgram(sourceFile : ts.SourceFile, program : ts.Program): Lint.RuleFailure[] { const walker : Lint.RuleWalker = new NoStringParameterToFunctionCallWalker( - sourceFile , 'setTimeout', this.getOptions(), languageService + sourceFile , 'setTimeout', this.getOptions(), program ); return this.applyWithWalker(walker); } diff --git a/src/noSuspiciousCommentRule.ts b/src/noSuspiciousCommentRule.ts index e53172cf3..16df2b37e 100644 --- a/src/noSuspiciousCommentRule.ts +++ b/src/noSuspiciousCommentRule.ts @@ -1,8 +1,8 @@ import * as ts from 'typescript'; import * as Lint from 'tslint'; +import {forEachTokenWithTrivia} from 'tsutils'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; -import {AstUtils} from './utils/AstUtils'; const FAILURE_STRING: string = 'Suspicious comment found: '; const SUSPICIOUS_WORDS = ['BUG', 'HACK', 'FIXME', 'LATER', 'LATER2', 'TODO']; @@ -32,30 +32,13 @@ export class Rule extends Lint.Rules.AbstractRule { } } -class NoSuspiciousCommentRuleWalker extends Lint.SkippableTokenAwareRuleWalker { +class NoSuspiciousCommentRuleWalker extends Lint.RuleWalker { public visitSourceFile(node: ts.SourceFile) { - const scanner: ts.Scanner = ts.createScanner( - ts.ScriptTarget.ES5, - false, // do not skip comments - AstUtils.getLanguageVariant(node), - node.text - ); - Lint.scanAllTokens(scanner, (scanner: ts.Scanner): void => { - const startPos = scanner.getStartPos(); - if ((this).tokensToSkipStartEndMap[startPos] != null) { - // tokens to skip are places where the scanner gets confused about what the token is, without - // the proper context (specifically, regex, identifiers, and templates). So skip those tokens. - scanner.setTextPos((this).tokensToSkipStartEndMap[startPos]); - return; - } - - if (scanner.getToken() === ts.SyntaxKind.SingleLineCommentTrivia || - scanner.getToken() === ts.SyntaxKind.MultiLineCommentTrivia) { - const commentText: string = scanner.getTokenText(); - const startPosition: number = scanner.getTokenPos(); - - this.scanCommentForSuspiciousWords(startPosition, commentText); + forEachTokenWithTrivia(node, (text, tokenSyntaxKind, range) => { + if (tokenSyntaxKind === ts.SyntaxKind.SingleLineCommentTrivia || + tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia) { + this.scanCommentForSuspiciousWords(range.pos, text.substring(range.pos, range.end)); } }); } @@ -76,6 +59,6 @@ class NoSuspiciousCommentRuleWalker extends Lint.SkippableTokenAwareRuleWalker { private foundSuspiciousComment(startPosition: number, commentText: string, suspiciousWord: string) { const errorMessage: string = FAILURE_STRING + suspiciousWord; - this.addFailure(this.createFailure(startPosition, commentText.length, errorMessage)); + this.addFailureAt(startPosition, commentText.length, errorMessage); } } diff --git a/src/noTypeofUndefinedRule.ts b/src/noTypeofUndefinedRule.ts index 066f3f712..2d8e72e13 100644 --- a/src/noTypeofUndefinedRule.ts +++ b/src/noTypeofUndefinedRule.ts @@ -38,7 +38,7 @@ class NoTypeofUndefinedRuleWalker extends ErrorTolerantWalker { if ((this.isUndefinedString(node.left) && this.isTypeOfExpression(node.right)) || this.isUndefinedString(node.right) && this.isTypeOfExpression(node.left)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText()); } super.visitBinaryExpression(node); } diff --git a/src/noUnexternalizedStringsRule.ts b/src/noUnexternalizedStringsRule.ts index 2102b90c6..bc339d4c5 100644 --- a/src/noUnexternalizedStringsRule.ts +++ b/src/noUnexternalizedStringsRule.ts @@ -87,7 +87,7 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { return; } if (!callInfo || callInfo.argIndex === -1 || !this.signatures[callInfo.callExpression.expression.getText()]) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`)); + this.addFailureAt(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`); return; } // We have a string that is a direct argument into the localize call. @@ -95,9 +95,9 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { ? callInfo.callExpression.arguments[this.messageIndex] : null; if (messageArg && messageArg !== node) { - this.addFailure(this.createFailure( + this.addFailureAt( messageArg.getStart(), messageArg.getWidth(), - `Message argument to '${callInfo.callExpression.expression.getText()}' must be a string literal.`)); + `Message argument to '${callInfo.callExpression.expression.getText()}' must be a string literal.`); return; } } @@ -121,5 +121,6 @@ class NoUnexternalizedStringsRuleWalker extends ErrorTolerantWalker { } node = parent; } + return null; } } \ No newline at end of file diff --git a/src/noUnnecessaryBindRule.ts b/src/noUnnecessaryBindRule.ts index f2add91d1..4fa9f5ca4 100644 --- a/src/noUnnecessaryBindRule.ts +++ b/src/noUnnecessaryBindRule.ts @@ -58,9 +58,9 @@ class NoUnnecessaryBindRuleWalker extends ErrorTolerantWalker { } if (contextArgument.getText() === 'this') { if (isArrowFunction(functionArgument)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_ARROW_WITH_BIND)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_ARROW_WITH_BIND); } else if (isFunctionLiteral(functionArgument)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_FUNCTION_WITH_BIND)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_FUNCTION_WITH_BIND); } } } diff --git a/src/noUnnecessaryFieldInitializationRule.ts b/src/noUnnecessaryFieldInitializationRule.ts index efa634fd1..1e615af33 100644 --- a/src/noUnnecessaryFieldInitializationRule.ts +++ b/src/noUnnecessaryFieldInitializationRule.ts @@ -64,7 +64,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { // you should never initialize a field to undefined. const start: number = initializer.getStart(); const width: number = initializer.getWidth(); - this.addFailure(this.createFailure(start, width, FAILURE_UNDEFINED_INIT + node.name.getText())); + this.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + node.name.getText()); } } @@ -88,7 +88,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { if (fieldInitValue == null) { const start: number = property.getStart(); const width: number = property.getWidth(); - this.addFailure(this.createFailure(start, width, FAILURE_UNDEFINED_INIT + property.getText())); + this.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + property.getText()); } } } else if (AstUtils.isConstant(binaryExpression.right)) { @@ -98,7 +98,7 @@ class UnnecessaryFieldInitializationRuleWalker extends ErrorTolerantWalker { const start: number = binaryExpression.getStart(); const width: number = binaryExpression.getWidth(); const message: string = FAILURE_UNDEFINED_DUPE + binaryExpression.getText(); - this.addFailure(this.createFailure(start, width, message)); + this.addFailureAt(start, width, message); } } } diff --git a/src/noUnnecessaryLocalVariableRule.ts b/src/noUnnecessaryLocalVariableRule.ts index c69fd42ff..63beb727c 100644 --- a/src/noUnnecessaryLocalVariableRule.ts +++ b/src/noUnnecessaryLocalVariableRule.ts @@ -72,8 +72,8 @@ class UnnecessaryLocalVariableRuleWalker extends ErrorTolerantWalker { if (returnedVariableName != null && declaredVariableName != null) { if (returnedVariableName === declaredVariableName) { - this.addFailure(this.createFailure(nextToLastStatement.getStart(), nextToLastStatement.getWidth(), - FAILURE_STRING + returnedVariableName)); + this.addFailureAt(nextToLastStatement.getStart(), nextToLastStatement.getWidth(), + FAILURE_STRING + returnedVariableName); } } } diff --git a/src/noUnnecessaryOverrideRule.ts b/src/noUnnecessaryOverrideRule.ts index e7355f73f..a89d306fb 100644 --- a/src/noUnnecessaryOverrideRule.ts +++ b/src/noUnnecessaryOverrideRule.ts @@ -37,7 +37,7 @@ class NoUnnecessaryOverrideRuleWalker extends ErrorTolerantWalker { const statement: ts.Statement = this.getSingleStatement(node.body); if (statement != null) { if (this.isSuperCall(node, statement) && this.isMatchingArgumentList(node, statement)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + this.getMethodName(node))); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + this.getMethodName(node)); } } } diff --git a/src/noUnnecessarySemicolonsRule.ts b/src/noUnnecessarySemicolonsRule.ts index 22f39b778..d90aa070a 100644 --- a/src/noUnnecessarySemicolonsRule.ts +++ b/src/noUnnecessarySemicolonsRule.ts @@ -34,7 +34,7 @@ class NoUnnecessarySemicolonsWalker extends ErrorTolerantWalker { protected visitNode(node: ts.Node): void { if (node.kind === ts.SyntaxKind.EmptyStatement) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } super.visitNode(node); } diff --git a/src/noUnsupportedBrowserCodeRule.ts b/src/noUnsupportedBrowserCodeRule.ts index 4f0895fc0..4e3c66baf 100644 --- a/src/noUnsupportedBrowserCodeRule.ts +++ b/src/noUnsupportedBrowserCodeRule.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import * as Lint from 'tslint'; -import {AstUtils} from './utils/AstUtils'; +import {forEachTokenWithTrivia} from 'tsutils'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; const UNSPECIFIED_BROWSER_VERSION: string = 'unspecified version'; @@ -40,7 +40,7 @@ export class Rule extends Lint.Rules.AbstractRule { } } -class NoUnsupportedBrowserCodeRuleWalker extends Lint.SkippableTokenAwareRuleWalker { +class NoUnsupportedBrowserCodeRuleWalker extends Lint.RuleWalker { private supportedBrowsers: { [key: string]: BrowserVersion }; constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) { @@ -51,41 +51,22 @@ class NoUnsupportedBrowserCodeRuleWalker extends Lint.SkippableTokenAwareRuleWal protected visitSourceFile(node: ts.SourceFile): void { // do not call super.visitSourceFile because we're already scanning the full file below - const scanner = ts.createScanner( - ts.ScriptTarget.ES5, - false, - AstUtils.getLanguageVariant(node), - node.text - ); - - Lint.scanAllTokens(scanner, (scanner: ts.Scanner) => { - const startPos = scanner.getStartPos(); - if ((this).tokensToSkipStartEndMap[startPos] != null) { - // tokens to skip are places where the scanner gets confused - // about what the token is, without the proper context - // (specifically, regex, identifiers, and templates). So skip - // those tokens. - scanner.setTextPos((this).tokensToSkipStartEndMap[startPos]); - return; - } - + forEachTokenWithTrivia(node, (text, tokenSyntaxKind, range) => { let regex; - if (scanner.getToken() === ts.SyntaxKind.MultiLineCommentTrivia) { + if (tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia) { regex = new RegExp(`${JSDOC_BROWSERSPECIFIC}\\s*(.*)`, 'gi'); - } else if (scanner.getToken() === ts.SyntaxKind.SingleLineCommentTrivia) { + } else if (tokenSyntaxKind === ts.SyntaxKind.SingleLineCommentTrivia) { regex = new RegExp(`${COMMENT_BROWSERSPECIFIC}\\s*(.*)`, 'gi'); } else { return; } let match; - /* tslint:disable-next-line:no-conditional-assignment */ - while ((match = regex.exec(scanner.getTokenText()))) { + const tokenText = text.substring(range.pos, range.end); + // tslint:disable-next-line:no-conditional-assignment + while ((match = regex.exec(tokenText))) { const browser = this.parseBrowserString(match[1]); - const startPos = scanner.getTokenPos() + match.index; - const length = match[0].length; - - this.findUnsupportedBrowserFailures(browser, startPos, length); + this.findUnsupportedBrowserFailures(browser, range.pos, range.end - range.pos); } }); } @@ -150,17 +131,17 @@ class NoUnsupportedBrowserCodeRuleWalker extends Lint.SkippableTokenAwareRuleWal private findUnsupportedBrowserFailures(targetBrowser: BrowserVersion, startPos: number, length: number) { if (!this.isSupportedBrowser(targetBrowser)) { - this.addFailure(this.createFailure( + this.addFailureAt( startPos, length, `${FAILURE_BROWSER_STRING}: ${targetBrowser.name}` - )); + ); } else if (!this.isSupportedBrowserVersion(targetBrowser)) { - this.addFailure(this.createFailure( + this.addFailureAt( startPos, length, `${FAILURE_VERSION_STRING}: ${targetBrowser.name} ${targetBrowser.version}` - )); + ); } } } diff --git a/src/noUnusedImportsRule.ts b/src/noUnusedImportsRule.ts deleted file mode 100644 index 5d4489061..000000000 --- a/src/noUnusedImportsRule.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as ts from 'typescript'; -import * as Lint from 'tslint'; - -import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; -import {AstUtils} from './utils/AstUtils'; -import {ExtendedMetadata} from './utils/ExtendedMetadata'; - -type Import = ts.ImportEqualsDeclaration | ts.ImportDeclaration; - -/** - * Implementation of the no-unused-import rule. - */ -export class Rule extends Lint.Rules.AbstractRule { - public static FAILURE_STRING: string = 'unused import: '; - - public static metadata: ExtendedMetadata = { - ruleName: 'no-unused-imports', - type: 'maintainability', - description: 'Deprecated - This rule is now covered by TSLint\'s no-unused-variables rule', - options: null, - optionsDescription: '', - typescriptOnly: true, - issueClass: 'Ignored', - issueType: 'Warning', - severity: 'Low', - level: 'Opportunity for Excellence', - group: 'Deprecated', - recommendation: 'false, // use tslint no-unused-variable rule instead', - commonWeaknessEnumeration: '398, 710' - }; - - public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost('file.ts', sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); - return this.applyWithWalker(new NoUnusedImportsWalker(sourceFile, this.getOptions(), languageService)); - } -} - -class NoUnusedImportsWalker extends ErrorTolerantWalker { - private languageServices: ts.LanguageService; - private cachedSourceText: string; - - public constructor(sourceFile : ts.SourceFile, - options : Lint.IOptions, - languageServices : ts.LanguageService) { - super(sourceFile, options); - this.languageServices = languageServices; - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - if (!AstUtils.hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword)) { - this.validateReferencesForVariable(node); - } - super.visitImportEqualsDeclaration(node); - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - if (!AstUtils.hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword)) { - this.validateReferencesForVariable(node); - } - super.visitImportDeclaration(node); - } - - /** - * As described in https://github.com/palantir/tslint/issues/325, - * the language service in typescript 1.4 doesn't seem to search for references in other imports. - * This will be fixed, but we can work around it by keeping track for the import references manually. - */ - private validateReferencesForVariable(node: Import) { - - if (this.isTsxFile() && this.isReactImport(node)) { - // react must be imported into tsx components - return; - } - - const variableStack: { name: string; position: number; importNode: Import; }[] = []; - if (node.kind === ts.SyntaxKind.ImportEqualsDeclaration) { - const name: string = (node).name.text; - const position: number = (node).name.getStart(); - variableStack.push({ name: name, position: position, importNode: node }); - } else { - const importClause: ts.ImportClause = (node).importClause; - if (importClause != null) { - if (importClause.name != null) { - const name: string = importClause.name.text; - const position: number = importClause.getStart(); - variableStack.push({ name: name, position: position, importNode: node }); - } else if (importClause.namedBindings != null) { - if (importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport) { - const imports: ts.NamespaceImport = importClause.namedBindings; - const name: string = imports.name.text; - const position: number = imports.name.getStart(); - variableStack.push({ name: name, position: position, importNode: node }); - } else if (importClause.namedBindings.kind === ts.SyntaxKind.NamedImports) { - const imports: ts.NamedImports = importClause.namedBindings; - imports.elements.forEach((importSpec: ts.ImportSpecifier): void => { - const name: string = importSpec.name.text; - const position: number = importSpec.name.getStart(); - variableStack.push({ name: name, position: position, importNode: node }); - }); - } - } - } - } - - variableStack.forEach((variable: { name: string; position: number; importNode: Import; }): void => { - const name: string = variable.name; - const position: number = variable.position; - const references = this.languageServices.getReferencesAtPosition('file.ts', position); - if (references.length <= 1) { - // there is a bug in how the language services finds nodes in ts and tsx files - const sourceText: string = this.getSourceText(); - const endOfImport: number = variable.importNode.getEnd(); - const restOfFile: string = sourceText.substring(endOfImport); - if (new RegExp('\\b(' + name + ')\\b', 'm').test(restOfFile)) { - return; - } - const failureString = Rule.FAILURE_STRING + '\'' + name + '\''; - const failure = this.createFailure(position, name.length, failureString); - this.addFailure(failure); - } - }); - } - - private getSourceText(): string { - if (this.cachedSourceText == null) { - this.cachedSourceText = this.getSourceFile().text; - } - return this.cachedSourceText; - } - - private isReactImport(node: Import): boolean { - if (node.kind === ts.SyntaxKind.ImportEqualsDeclaration) { - const importDeclaration: ts.ImportEqualsDeclaration = node; - if (importDeclaration.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - const moduleExpression: ts.Expression = (importDeclaration.moduleReference).expression; - return this.isModuleExpressionReact(moduleExpression); - } - } else if (node.kind === ts.SyntaxKind.ImportDeclaration) { - const importDeclaration: ts.ImportDeclaration = node; - const moduleExpression: ts.Expression = importDeclaration.moduleSpecifier; - return this.isModuleExpressionReact(moduleExpression); - } - return false; - } - - private isModuleExpressionReact(moduleExpression: ts.Expression): boolean { - if (moduleExpression != null && moduleExpression.kind === ts.SyntaxKind.StringLiteral) { - const moduleName: ts.StringLiteral = moduleExpression; - return /react/i.test(moduleName.text); - } - return false; - } - - private isTsxFile(): boolean { - return /.*\.tsx/.test(this.getSourceFile().fileName); - } -} diff --git a/src/noVarSelfRule.ts b/src/noVarSelfRule.ts index 6ecfb8ba2..2e8e3b995 100644 --- a/src/noVarSelfRule.ts +++ b/src/noVarSelfRule.ts @@ -25,7 +25,13 @@ export class Rule extends Lint.Rules.AbstractRule { commonWeaknessEnumeration: '398, 710' }; + private static isWarningShown: boolean = false; + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + if (Rule.isWarningShown === false) { + console.warn('Warning: no-var-self rule is deprecated. Replace your usage with the TSLint no-this-assignment rule.'); + Rule.isWarningShown = true; + } return this.applyWithWalker(new NoVarSelfRuleWalker(sourceFile, this.getOptions())); } } @@ -46,7 +52,7 @@ class NoVarSelfRuleWalker extends Lint.RuleWalker { if (node.name.kind === ts.SyntaxKind.Identifier) { const identifier: ts.Identifier = node.name; if (this.bannedVariableNames.test(identifier.text)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText()); } } } diff --git a/src/noWithStatementRule.ts b/src/noWithStatementRule.ts index 2310b6469..9f00a2214 100644 --- a/src/noWithStatementRule.ts +++ b/src/noWithStatementRule.ts @@ -34,7 +34,7 @@ export class Rule extends Lint.Rules.AbstractRule { class NoWithStatementWalker extends ErrorTolerantWalker { protected visitNode(node: ts.Node): void { if (node.kind === ts.SyntaxKind.WithStatement) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); } super.visitNode(node); } diff --git a/src/nonLiteralRequireRule.ts b/src/nonLiteralRequireRule.ts index e470d4726..ffde07983 100644 --- a/src/nonLiteralRequireRule.ts +++ b/src/nonLiteralRequireRule.ts @@ -58,7 +58,7 @@ class NonLiteralRequireRuleWalker extends ErrorTolerantWalker { const start: number = expression.getStart(); const width: number = expression.getWidth(); const message: string = FAILURE_STRING + Utils.trimTo(expression.getText(), 25); - this.addFailure(this.createFailure(start, width, message)); + this.addFailureAt(start, width, message); } } diff --git a/src/possibleTimingAttackRule.ts b/src/possibleTimingAttackRule.ts index 4c7dfc4af..c162bd79c 100644 --- a/src/possibleTimingAttackRule.ts +++ b/src/possibleTimingAttackRule.ts @@ -44,7 +44,7 @@ class PossibleTimingAttackRuleWalker extends ErrorTolerantWalker { if ((SENSITIVE_VAR_NAME.test(node.left.getText()) || SENSITIVE_VAR_NAME.test(node.right.getText())) && node.left.getText() !== 'null' && node.right.getText() !== 'null' && node.left.getText() !== 'undefined' && node.right.getText() !== 'undefined') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + Utils.trimTo(node.getText(), 20))); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + Utils.trimTo(node.getText(), 20)); } else { super.visitBinaryExpression(node); } diff --git a/src/preferArrayLiteralRule.ts b/src/preferArrayLiteralRule.ts index 021419d17..4e444891c 100644 --- a/src/preferArrayLiteralRule.ts +++ b/src/preferArrayLiteralRule.ts @@ -50,8 +50,7 @@ class NoGenericArrayWalker extends ErrorTolerantWalker { if (this.allowTypeParameters === false) { if ((node.typeName).text === 'Array') { const failureString = Rule.GENERICS_FAILURE_STRING + node.getText(); - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } } super.visitTypeReference(node); @@ -61,7 +60,7 @@ class NoGenericArrayWalker extends ErrorTolerantWalker { const functionName = AstUtils.getFunctionName(node); if (functionName === 'Array') { const failureString = Rule.CONSTRUCTOR_FAILURE_STRING + node.getText(); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), failureString)); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } super.visitNewExpression(node); } diff --git a/src/preferTypeCastRule.ts b/src/preferTypeCastRule.ts index 12d1ffb32..381ad6d56 100644 --- a/src/preferTypeCastRule.ts +++ b/src/preferTypeCastRule.ts @@ -41,7 +41,7 @@ class PreferTypeCastRuleWalker extends ErrorTolerantWalker { protected visitNode(node: ts.Node): void { if (node.kind === ts.SyntaxKind.AsExpression) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STRING + node.getText()); } super.visitNode(node); } diff --git a/src/promiseMustCompleteRule.ts b/src/promiseMustCompleteRule.ts index ce3a306eb..02e43838d 100644 --- a/src/promiseMustCompleteRule.ts +++ b/src/promiseMustCompleteRule.ts @@ -77,8 +77,7 @@ class PromiseAnalyzer extends ErrorTolerantWalker { const blockAnalyzer = new PromiseCompletionWalker(this.getSourceFile(), this.getOptions(), completionIdentifiers); blockAnalyzer.visitNode(block); if (!blockAnalyzer.isAlwaysCompleted()) { - const failure = this.createFailure(promiseInstantiation.getStart(), promiseInstantiation.getWidth(), Rule.FAILURE_STRING); - this.addFailure(failure); + this.addFailureAt(promiseInstantiation.getStart(), promiseInstantiation.getWidth(), Rule.FAILURE_STRING); } } } diff --git a/src/reactA11yAnchorsRule.ts b/src/reactA11yAnchorsRule.ts index f1c6db9b5..073fd05e4 100644 --- a/src/reactA11yAnchorsRule.ts +++ b/src/reactA11yAnchorsRule.ts @@ -74,8 +74,8 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { // Same href - different text... sameHrefDifferentTexts.push(anchorInfo); - this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, - SAME_HREF_SAME_TEXT_FAILURE_STRING + this.firstPosition(current))); + this.addFailureAt(anchorInfo.start, anchorInfo.width, + SAME_HREF_SAME_TEXT_FAILURE_STRING + this.firstPosition(current)); } if (current.href !== anchorInfo.href && @@ -85,8 +85,8 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { // Different href - same text... differentHrefSameText.push(anchorInfo); - this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, - DIFFERENT_HREF_DIFFERENT_TEXT_FAILURE_STRING + this.firstPosition(current))); + this.addFailureAt(anchorInfo.start, anchorInfo.width, + DIFFERENT_HREF_DIFFERENT_TEXT_FAILURE_STRING + this.firstPosition(current)); } }); } @@ -125,11 +125,11 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { }; if (anchorInfo.href === '#') { - this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, NO_HASH_FAILURE_STRING)); + this.addFailureAt(anchorInfo.start, anchorInfo.width, NO_HASH_FAILURE_STRING); } if (anchorInfo.altText && anchorInfo.altText === anchorInfo.text) { - this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, UNIQUE_ALT_FAILURE_STRING)); + this.addFailureAt(anchorInfo.start, anchorInfo.width, UNIQUE_ALT_FAILURE_STRING); } const anchorInfoTextLength: number = anchorInfo.text ? anchorInfo.text.length : 0; @@ -140,7 +140,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { anchorInfoTextLength < 4 && anchorImageAltTextLength < 4 ) { - this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, LINK_TEXT_TOO_SHORT_FAILURE_STRING)); + this.addFailureAt(anchorInfo.start, anchorInfo.width, LINK_TEXT_TOO_SHORT_FAILURE_STRING); } this.anchorInfoList.push(anchorInfo); diff --git a/src/reactA11yAriaUnsupportedElementsRule.ts b/src/reactA11yAriaUnsupportedElementsRule.ts index c05f167b5..bd48655a4 100644 --- a/src/reactA11yAriaUnsupportedElementsRule.ts +++ b/src/reactA11yAriaUnsupportedElementsRule.ts @@ -74,7 +74,7 @@ class ReactA11yAriaUnsupportedElementsWalker extends Lint.RuleWalker { if (invalidAttributeNames.length > 0) { const message: string = getFailureString(tagName, invalidAttributeNames); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + this.addFailureAt(node.getStart(), node.getWidth(), message); } } } diff --git a/src/reactA11yEventHasRoleRule.ts b/src/reactA11yEventHasRoleRule.ts index f8187e2e4..8f3e13197 100644 --- a/src/reactA11yEventHasRoleRule.ts +++ b/src/reactA11yEventHasRoleRule.ts @@ -61,11 +61,11 @@ class ReactA11yEventHasRoleWalker extends Lint.RuleWalker { const hasAriaRole: boolean = !!attributes[ROLE_STRING] || !!getImplicitRole(node); if (events.length > 0 && !hasAriaRole) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), FAILURE_STRING - )); + ); } } } diff --git a/src/reactA11yImageButtonHasAltRule.ts b/src/reactA11yImageButtonHasAltRule.ts index 5b9284866..5049b60de 100644 --- a/src/reactA11yImageButtonHasAltRule.ts +++ b/src/reactA11yImageButtonHasAltRule.ts @@ -62,9 +62,9 @@ class ReactA11yImageButtonHasAltWalker extends Lint.RuleWalker { const altAttribute: ts.JsxAttribute = attributes[ALT_STRING]; if (!altAttribute) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), NO_ALT_ATTRIBUTE_FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), NO_ALT_ATTRIBUTE_FAILURE_STRING); } else if (isEmpty(altAttribute) || !getStringLiteral(altAttribute)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), EMPTY_ALT_ATTRIBUTE_FAILURE_STRING)); + this.addFailureAt(node.getStart(), node.getWidth(), EMPTY_ALT_ATTRIBUTE_FAILURE_STRING); } } } diff --git a/src/reactA11yImgHasAltRule.ts b/src/reactA11yImgHasAltRule.ts index 19e393be2..6aaf4ef46 100644 --- a/src/reactA11yImgHasAltRule.ts +++ b/src/reactA11yImgHasAltRule.ts @@ -92,11 +92,11 @@ class ImgHasAltWalker extends Lint.RuleWalker { const altAttribute: ts.JsxAttribute = attributes[ALT_STRING]; if (!altAttribute) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureStringNoAlt(tagName) - )); + ); } else { const roleAttribute: ts.JsxAttribute = attributes[ROLE_STRING]; const roleAttributeValue: string = roleAttribute ? getStringLiteral(roleAttribute) : ''; @@ -108,17 +108,17 @@ class ImgHasAltWalker extends Lint.RuleWalker { // altValue if (!isEmptyAlt && isPresentationRole && !allowNonEmptyAltWithRolePresentation) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureStringNonEmptyAltAndPresentationRole(tagName) - )); + ); } else if (isEmptyAlt && !isPresentationRole) { // - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureStringEmptyAltAndNotPresentationRole(tagName) - )); + ); } } } diff --git a/src/reactA11yLangRule.ts b/src/reactA11yLangRule.ts index ebf440090..238d991b5 100644 --- a/src/reactA11yLangRule.ts +++ b/src/reactA11yLangRule.ts @@ -63,28 +63,28 @@ class ReactA11yLangRuleWalker extends ErrorTolerantWalker { private validateOpeningElement(parent: ts.Node, openingElement: ts.JsxOpeningLikeElement): void { if (openingElement.tagName.getText() === 'html') { - const attributes: ts.NodeArray = openingElement.attributes; + const attributes: ts.JsxAttributes = openingElement.attributes; let langFound: boolean = false; - attributes.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { + attributes.properties.forEach((attribute: ts.JsxAttributeLike): void => { if (attribute.kind === ts.SyntaxKind.JsxAttribute) { if ((attribute).name.getText() === 'lang') { langFound = true; if ((attribute).initializer.kind === ts.SyntaxKind.StringLiteral) { const langText: string = ((attribute).initializer).text; if ((LANGUAGE_CODES.indexOf(langText)) === -1) { - this.addFailure(this.createFailure( + this.addFailureAt( parent.getStart(), parent.getWidth(), FAILURE_WRONG_LANG_CODE + langText - )); + ); } } } } }); if (!langFound) { - this.addFailure(this.createFailure(parent.getStart(), parent.getWidth(), FAILURE_MISSING_LANG)); + this.addFailureAt(parent.getStart(), parent.getWidth(), FAILURE_MISSING_LANG); } } } diff --git a/src/reactA11yMetaRule.ts b/src/reactA11yMetaRule.ts index e33d1f1d4..abf3384a5 100644 --- a/src/reactA11yMetaRule.ts +++ b/src/reactA11yMetaRule.ts @@ -47,13 +47,13 @@ class ReactA11yMetaRuleWalker extends ErrorTolerantWalker { private validateOpeningElement(parent: ts.Node, openElement: ts.JsxOpeningLikeElement): void { if (openElement.tagName.getText() === 'meta') { - const attributes: ts.NodeArray = openElement.attributes; - attributes.forEach((parameter: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { + const attributes: ts.JsxAttributes = openElement.attributes; + attributes.properties.forEach((parameter: ts.JsxAttributeLike): void => { if (parameter.kind === ts.SyntaxKind.JsxAttribute) { const attribute: ts.JsxAttribute = parameter; if (attribute.name.getText() === 'http-equiv') { if (this.isStringLiteral(attribute.initializer, 'refresh')) { - this.addFailure(this.createFailure(parent.getStart(), openElement.getWidth(), FAILURE_STRING)); + this.addFailureAt(parent.getStart(), openElement.getWidth(), FAILURE_STRING); } } } diff --git a/src/reactA11yPropsRule.ts b/src/reactA11yPropsRule.ts index 6dbee659f..2531d645e 100644 --- a/src/reactA11yPropsRule.ts +++ b/src/reactA11yPropsRule.ts @@ -49,11 +49,11 @@ class A11yPropsWalker extends Lint.RuleWalker { } if (!ARIA_SCHEMA[name.toLowerCase()]) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureString(name) - )); + ); } } } diff --git a/src/reactA11yProptypesRule.ts b/src/reactA11yProptypesRule.ts index e10308190..9b6fa2560 100644 --- a/src/reactA11yProptypesRule.ts +++ b/src/reactA11yProptypesRule.ts @@ -82,9 +82,9 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { if (this.isUndefined(node.initializer)) { if (!allowUndefined) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureString(propName, expectedType, permittedValues) - )); + ); } return; } else if (this.isComplexType(node.initializer)) { @@ -92,11 +92,11 @@ class ReactA11yProptypesWalker extends Lint.RuleWalker { } if (!this.validityCheck(node.initializer, propValue, expectedType, permittedValues)) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), getFailureString(propName, expectedType, permittedValues) - )); + ); } } diff --git a/src/reactA11yRoleHasRequiredAriaPropsRule.ts b/src/reactA11yRoleHasRequiredAriaPropsRule.ts index 8553d7000..d83b68108 100644 --- a/src/reactA11yRoleHasRequiredAriaPropsRule.ts +++ b/src/reactA11yRoleHasRequiredAriaPropsRule.ts @@ -104,13 +104,13 @@ class A11yRoleHasRequiredAriaPropsWalker extends Lint.RuleWalker { .filter((attributeName: string) => attributeNamesInElement.indexOf(attributeName) === -1); if (missingAttributes.length > 0) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), isImplicitRole ? getFailureStringForImplicitRole(node.tagName.getText(), normalizedRoles[0], missingAttributes) : getFailureStringForNotImplicitRole(normalizedRoles, missingAttributes) - )); + ); } } } diff --git a/src/reactA11yRoleRule.ts b/src/reactA11yRoleRule.ts index c7190b88f..a3a616188 100644 --- a/src/reactA11yRoleRule.ts +++ b/src/reactA11yRoleRule.ts @@ -65,10 +65,10 @@ class A11yRoleRuleWalker extends Lint.RuleWalker { const normalizedValues: string[] = roleValue.toLowerCase().split(' '); if (normalizedValues.some(value => value && VALID_ROLES.indexOf(value) === -1)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), getFailureStringInvalidRole(roleValue))); + this.addFailureAt(node.getStart(), node.getWidth(), getFailureStringInvalidRole(roleValue)); } } else if (roleValue === '' || isEmpty(node)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), getFailureStringUndefinedRole())); + this.addFailureAt(node.getStart(), node.getWidth(), getFailureStringUndefinedRole()); } super.visitJsxAttribute(node); diff --git a/src/reactA11yRoleSupportsAriaPropsRule.ts b/src/reactA11yRoleSupportsAriaPropsRule.ts index fb768014f..e36806ce9 100644 --- a/src/reactA11yRoleSupportsAriaPropsRule.ts +++ b/src/reactA11yRoleSupportsAriaPropsRule.ts @@ -118,11 +118,11 @@ class A11yRoleSupportsAriaPropsWalker extends Lint.RuleWalker { } if (invalidAttributeNamesInElement.length > 0) { - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart(), node.getWidth(), failureString - )); + ); } } } diff --git a/src/reactA11yTabindexNoPositiveRule.ts b/src/reactA11yTabindexNoPositiveRule.ts index 705960906..536c95f51 100644 --- a/src/reactA11yTabindexNoPositiveRule.ts +++ b/src/reactA11yTabindexNoPositiveRule.ts @@ -46,11 +46,11 @@ class A11yTabindexNoPositiveWalker extends Lint.RuleWalker { // In case the attribute has no value of empty value. if (literalString === '') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), getFailureString())); + this.addFailureAt(node.getStart(), node.getWidth(), getFailureString()); } else if (literalString && literalString !== '-1' && literalString !== '0') { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), getFailureString())); + this.addFailureAt(node.getStart(), node.getWidth(), getFailureString()); } else if (isEmpty(node)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), getFailureString())); + this.addFailureAt(node.getStart(), node.getWidth(), getFailureString()); } } } diff --git a/src/reactA11yTitlesRule.ts b/src/reactA11yTitlesRule.ts index 84145779d..98d996d63 100644 --- a/src/reactA11yTitlesRule.ts +++ b/src/reactA11yTitlesRule.ts @@ -42,8 +42,8 @@ class ReactA11yTitlesRuleWalker extends ErrorTolerantWalker { protected visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): void { if (node.tagName.getText() === 'title') { - this.addFailure(this.createFailure(node.getStart(), - node.getWidth(), EMPTY_TITLE_FAILURE_STRING)); + this.addFailureAt(node.getStart(), + node.getWidth(), EMPTY_TITLE_FAILURE_STRING); } super.visitJsxSelfClosingElement(node); } @@ -52,8 +52,8 @@ class ReactA11yTitlesRuleWalker extends ErrorTolerantWalker { const openingElement: ts.JsxOpeningElement = node.openingElement; if (openingElement.tagName.getText() === 'title') { if (node.children.length === 0) { - this.addFailure(this.createFailure(node.getStart(), - node.getWidth(), EMPTY_TITLE_FAILURE_STRING)); + this.addFailureAt(node.getStart(), + node.getWidth(), EMPTY_TITLE_FAILURE_STRING); } else if (node.children.length === 1) { if (node.children[0].kind === ts.SyntaxKind.JsxText) { const value: ts.JsxText = node.children[0]; @@ -71,15 +71,15 @@ class ReactA11yTitlesRuleWalker extends ErrorTolerantWalker { private validateTitleText(text: string, titleNode: ts.Node): void { if (text.length > MAX_TITLE_LENGTH) { - this.addFailure(this.createFailure( + this.addFailureAt( titleNode.getStart(), titleNode.getWidth(), - LONG_TITLE_FAILURE_STRING + ': ' + Utils.trimTo(text, 20))); + LONG_TITLE_FAILURE_STRING + ': ' + Utils.trimTo(text, 20)); } else if (!(text.indexOf(' ') > 0)) { - this.addFailure(this.createFailure( + this.addFailureAt( titleNode.getStart(), titleNode.getWidth(), - WORD_TITLE_FAILURE_STRING + ': ' + Utils.trimTo(text, 20))); + WORD_TITLE_FAILURE_STRING + ': ' + Utils.trimTo(text, 20)); } } } diff --git a/src/reactAnchorBlankNoopenerRule.ts b/src/reactAnchorBlankNoopenerRule.ts index 914296307..7fbb4bf91 100644 --- a/src/reactAnchorBlankNoopenerRule.ts +++ b/src/reactAnchorBlankNoopenerRule.ts @@ -62,7 +62,7 @@ class ReactAnchorBlankNoopenerRuleWalker extends ErrorTolerantWalker { const rel: ts.JsxAttribute = allAttributes['rel']; /* tslint:enable:no-string-literal */ if (target != null && getStringLiteral(target) === '_blank' && !isRelAttributeValue(rel)) { - this.addFailure(this.createFailure(openingElement.getStart(), openingElement.getWidth(), FAILURE_STRING)); + this.addFailureAt(openingElement.getStart(), openingElement.getWidth(), FAILURE_STRING); } } } diff --git a/src/reactIframeMissingSandboxRule.ts b/src/reactIframeMissingSandboxRule.ts index 2db40c100..dfe5cb009 100644 --- a/src/reactIframeMissingSandboxRule.ts +++ b/src/reactIframeMissingSandboxRule.ts @@ -72,7 +72,7 @@ class ReactIframeMissingSandboxRuleWalker extends ErrorTolerantWalker { } let sandboxAttributeFound: boolean = false; - node.attributes.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { + node.attributes.properties.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { if (attribute.kind === ts.SyntaxKind.JsxAttribute) { const jsxAttribute: ts.JsxAttribute = attribute; const attributeName = jsxAttribute.name.text; @@ -86,7 +86,7 @@ class ReactIframeMissingSandboxRuleWalker extends ErrorTolerantWalker { }); if (!sandboxAttributeFound) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_NOT_FOUND)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_NOT_FOUND); } } @@ -96,7 +96,7 @@ class ReactIframeMissingSandboxRuleWalker extends ErrorTolerantWalker { let allowSameOrigin: boolean = false; values.forEach((attributeValue: string): void => { if (ALLOWED_VALUES.indexOf(attributeValue) === -1) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_INVALID_ENTRY + attributeValue)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_INVALID_ENTRY + attributeValue); } if (attributeValue === 'allow-scripts') { allowScripts = true; @@ -106,7 +106,7 @@ class ReactIframeMissingSandboxRuleWalker extends ErrorTolerantWalker { } }); if (allowScripts && allowSameOrigin) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_INVALID_COMBINATION)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_INVALID_COMBINATION); } } } diff --git a/src/reactNoDangerousHtmlRule.ts b/src/reactNoDangerousHtmlRule.ts index ccf5a2cd5..82837fb7c 100644 --- a/src/reactNoDangerousHtmlRule.ts +++ b/src/reactNoDangerousHtmlRule.ts @@ -31,11 +31,7 @@ export class Rule extends Lint.Rules.AbstractRule { }; public apply(sourceFile : ts.SourceFile): Lint.RuleFailure[] { - const documentRegistry = ts.createDocumentRegistry(); - const languageServiceHost = Lint.createLanguageServiceHost(sourceFile.fileName, sourceFile.getFullText()); - const languageService = ts.createLanguageService(languageServiceHost, documentRegistry); - - return this.applyWithWalker(new NoDangerousHtmlWalker(sourceFile, this.getOptions(), languageService)); + return this.applyWithWalker(new NoDangerousHtmlWalker(sourceFile, this.getOptions())); } /** @@ -55,12 +51,10 @@ export class Rule extends Lint.Rules.AbstractRule { } class NoDangerousHtmlWalker extends ErrorTolerantWalker { - private languageServices : ts.LanguageService; private currentMethodName : string; - constructor(sourceFile : ts.SourceFile, options : Lint.IOptions, languageServices : ts.LanguageService) { + constructor(sourceFile : ts.SourceFile, options : Lint.IOptions) { super(sourceFile, options); - this.languageServices = languageServices; this.currentMethodName = ''; } @@ -93,7 +87,7 @@ class NoDangerousHtmlWalker extends ErrorTolerantWalker { } private handleJsxOpeningElement(node: ts.JsxOpeningLikeElement): void { - node.attributes.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { + node.attributes.properties.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { if (attribute.kind === ts.SyntaxKind.JsxAttribute) { const jsxAttribute: ts.JsxAttribute = attribute; const attributeName = jsxAttribute.name.text; @@ -112,8 +106,7 @@ class NoDangerousHtmlWalker extends ErrorTolerantWalker { ' to review the usage with a security expert/QE representative. If they decide that this is an\n' + ' acceptable usage then add the exception to xss_exceptions.json'; const position = parent.getStart(); - const failure = this.createFailure(position, node.text.length, failureString); - this.addFailure(failure); + this.addFailureAt(position, node.text.length, failureString); } } diff --git a/src/reactThisBindingIssueRule.ts b/src/reactThisBindingIssueRule.ts index 0d9960d87..868375440 100644 --- a/src/reactThisBindingIssueRule.ts +++ b/src/reactThisBindingIssueRule.ts @@ -125,7 +125,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { private visitJsxOpeningElement(node: ts.JsxOpeningLikeElement): void { // create violations if the listener is a reference to a class method that was not bound to 'this' in the constructor - node.attributes.forEach((attributeLikeElement: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { + node.attributes.properties.forEach((attributeLikeElement: ts.JsxAttribute | ts.JsxSpreadAttribute): void => { if (this.isUnboundListener(attributeLikeElement)) { const attribute: ts.JsxAttribute = attributeLikeElement; if (attribute.initializer.kind === ts.SyntaxKind.StringLiteral) { @@ -138,7 +138,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { const start: number = propAccess.getStart(); const widget: number = propAccess.getWidth(); const message: string = FAILURE_UNBOUND_LISTENER + listenerText; - this.addFailure(this.createFailure(start, widget, message)); + this.addFailureAt(start, widget, message); } } else if (this.isAttributeAnonymousFunction(attributeLikeElement)) { const attribute: ts.JsxAttribute = attributeLikeElement; @@ -150,7 +150,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { const start: number = expression.getStart(); const widget: number = expression.getWidth(); const message: string = FAILURE_ANONYMOUS_LISTENER + Utils.trimTo(expression.getText(), 30); - this.addFailure(this.createFailure(start, widget, message)); + this.addFailureAt(start, widget, message); } }); } @@ -246,7 +246,7 @@ class ReactThisBindingIssueRuleWalker extends ErrorTolerantWalker { const start = binaryExpression.getStart(); const width = binaryExpression.getWidth(); const msg = FAILURE_DOUBLE_BIND + binaryExpression.getText(); - this.addFailure(this.createFailure(start, width, msg)); + this.addFailureAt(start, width, msg); } } } diff --git a/src/reactTsxCurlySpacingRule.ts b/src/reactTsxCurlySpacingRule.ts index 54c436b56..b8d169b9f 100644 --- a/src/reactTsxCurlySpacingRule.ts +++ b/src/reactTsxCurlySpacingRule.ts @@ -112,7 +112,7 @@ class TsxCurlySpacingWalker extends Lint.RuleWalker { } private reportFailure(start: ts.Node, endNode: ts.Node, failure: string): void { - this.addFailure(this.createFailure(start.getStart(), endNode.getStart() - start.getStart(), failure)); + this.addFailureAt(start.getStart(), endNode.getStart() - start.getStart(), failure); } private isSpaceBetweenTokens(left: ts.Node, right: ts.Node): boolean { diff --git a/src/reactUnusedPropsAndStateRule.ts b/src/reactUnusedPropsAndStateRule.ts index a9c2f1fe1..6b02431e7 100644 --- a/src/reactUnusedPropsAndStateRule.ts +++ b/src/reactUnusedPropsAndStateRule.ts @@ -86,11 +86,11 @@ class ReactUnusedPropsAndStateRuleWalker extends ErrorTolerantWalker { this.propNames.forEach((propName: string): void => { const typeElement: ts.TypeElement = this.propNodes[propName]; - this.addFailure(this.createFailure(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_PROP + propName)); + this.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_PROP + propName); }); this.stateNames.forEach((stateName: string): void => { const typeElement: ts.TypeElement = this.stateNodes[stateName]; - this.addFailure(this.createFailure(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_STATE + stateName)); + this.addFailureAt(typeElement.getStart(), typeElement.getWidth(), FAILURE_UNUSED_STATE + stateName); }); } diff --git a/src/tests/FixNoRequireImportsFormatterTests.ts b/src/tests/FixNoRequireImportsFormatterTests.ts index cf228607e..a92648279 100644 --- a/src/tests/FixNoRequireImportsFormatterTests.ts +++ b/src/tests/FixNoRequireImportsFormatterTests.ts @@ -16,11 +16,12 @@ class FormatterForTesting extends Formatter { return this.output; } - protected readFile(fileName: string): string { + protected readFile(): string { return this.input; } - protected writeFile(fileName: string, fileContents: string): void { + // tslint:disable-next-line:variable-name + protected writeFile(_fileName: string, fileContents: string): void { this.output = fileContents; } } diff --git a/src/tests/FixNoUnusedImportsFormatterTests.ts b/src/tests/FixNoUnusedImportsFormatterTests.ts deleted file mode 100644 index bab247525..000000000 --- a/src/tests/FixNoUnusedImportsFormatterTests.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as chai from 'chai'; -import {TestHelper} from './TestHelper'; -import {Formatter} from '../fixNoUnusedImportsFormatter'; - -class FixNoUnusedImportsFormatterForTesting extends Formatter { - - private input: string; - private output: string; - - constructor(input: string) { - super(); - this.input = input; - } - - public getOutput(): string { - return this.output; - } - - protected readFile(fileName: string): string { - return this.input; - } - - protected writeFile(fileName: string, fileContents: string): void { - this.output = fileContents; - } -} - -/** - * Unit tests. - */ -describe('fixNoUnusedImportsFormatter', () : void => { - - const ruleName : string = 'no-unused-imports'; - - it('should fix an unused import at start of file', () : void => { - const input : string = `import Unused = require('Unused'); -class NoUnusedImportsTestInput { - constructor() { - } -}`; - - const formatter = new FixNoUnusedImportsFormatterForTesting(input); - formatter.format(TestHelper.runRule(ruleName, null, input).failures); - chai.expect(formatter.getOutput().trim()).to.equal( -`class NoUnusedImportsTestInput { - constructor() { - } -}`.trim()); - }); - - it('should fix an unused import at last import line', () : void => { - const input : string = `import Used = require('Used'); -import Unused = require('Unused'); - -class NoUnusedImportsTestInput { - constructor() { - console.log(Used); - } -}`; - - const formatter = new FixNoUnusedImportsFormatterForTesting(input); - formatter.format(TestHelper.runRule(ruleName, null, input).failures); - chai.expect(formatter.getOutput().trim()).to.equal( -`import Used = require('Used'); -class NoUnusedImportsTestInput { - constructor() { - console.log(Used); - } -}`); - }); - -}); \ No newline at end of file diff --git a/src/tests/FixNoVarKeywordFormatterTests.ts b/src/tests/FixNoVarKeywordFormatterTests.ts index 2d945a211..059f25593 100644 --- a/src/tests/FixNoVarKeywordFormatterTests.ts +++ b/src/tests/FixNoVarKeywordFormatterTests.ts @@ -17,11 +17,12 @@ class FixNoVarKeywordFormatterForTesting extends Formatter { return this.output; } - protected readFile(fileName: string): string { + protected readFile(): string { return this.input; } - protected writeFile(fileName: string, fileContents: string): void { + // tslint:disable-next-line:variable-name + protected writeFile(_fileName: string, fileContents: string): void { this.output = fileContents; } } diff --git a/src/tests/ImportNameRuleTests.ts b/src/tests/ImportNameRuleTests.ts index 4a4c9437f..6481a57b4 100644 --- a/src/tests/ImportNameRuleTests.ts +++ b/src/tests/ImportNameRuleTests.ts @@ -43,13 +43,23 @@ describe('importNameRule', () : void => { "failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 2 } + "startPosition": { "character": 13, "line": 2 }, + "fix": { + "innerStart": 20, + "innerLength": 9, + "innerText": "App" + } }, { "failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp2'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 3 } + "startPosition": { "character": 13, "line": 3 }, + "fix": { + "innerStart": 67, + "innerLength": 10, + "innerText": "App" + } } ]); }); @@ -65,13 +75,23 @@ describe('importNameRule', () : void => { "failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 2 } + "startPosition": { "character": 13, "line": 2 }, + "fix": { + "innerStart": 20, + "innerLength": 9, + "innerText": "App" + } }, { "failure": "Misnamed import. Import should be named 'App' but found 'MyCoolApp2'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 3 } + "startPosition": { "character": 13, "line": 3 }, + "fix": { + "innerStart": 61, + "innerLength": 10, + "innerText": "App" + } } ]); }); @@ -86,7 +106,12 @@ describe('importNameRule', () : void => { "failure": "Misnamed import. Import should be named 'DependencyManager' but found 'Service'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 2 } + "startPosition": { "character": 13, "line": 2 }, + "fix": { + "innerStart": 20, + "innerLength": 7, + "innerText": "DependencyManager" + } } ]); }); @@ -101,7 +126,12 @@ describe('importNameRule', () : void => { "failure": "Misnamed import. Import should be named 'userSettingsPage' but found 'UserSettings'", "name": "file.ts", "ruleName": "import-name", - "startPosition": { "character": 13, "line": 2 } + "startPosition": { "character": 13, "line": 2 }, + "fix": { + "innerStart": 20, + "innerLength": 12, + "innerText": "userSettingsPage" + } } ]); }); diff --git a/src/tests/MochaAvoidOnlyRuleTests.ts b/src/tests/MochaAvoidOnlyRuleTests.ts index eb58d6b45..fbec0150c 100644 --- a/src/tests/MochaAvoidOnlyRuleTests.ts +++ b/src/tests/MochaAvoidOnlyRuleTests.ts @@ -28,7 +28,7 @@ describe('mochaAvoidOnlyRule', () : void => { describe.only(something, () => {}); describe.only('', something); describe.only(something, somethingElse); - + // these are not calls to mocha's context.only context.only(); context.only(''); diff --git a/src/tests/NoCookiesTests.ts b/src/tests/NoCookiesTests.ts index 5b68ad4b0..e03ff648a 100644 --- a/src/tests/NoCookiesTests.ts +++ b/src/tests/NoCookiesTests.ts @@ -6,21 +6,8 @@ import {TestHelper} from './TestHelper'; describe('noCookiesRule', () : void => { it('should not produce violations', () : void => { const ruleName : string = 'no-cookies'; - const inputFile : string = ` -interface DocumentLikeAPI { - cookie: string; -} - -function documentLikeAPIFunction() : DocumentLikeAPI { - return null; -} - -// These usages are OK because they are not on the DOM document -var document : DocumentLikeAPI = documentLikeAPIFunction(); -document.cookie = '...'; -document.cookie = '...'; -documentLikeAPIFunction().cookie = '...';`; - TestHelper.assertViolations( + const inputFile : string = 'test-data/NoCookies/NoCookiesPassingTestInput.ts'; + TestHelper.assertViolationsWithTypeChecker( ruleName, inputFile, [ ] @@ -30,7 +17,7 @@ documentLikeAPIFunction().cookie = '...';`; it('should produce violations', () : void => { const ruleName : string = 'no-cookies'; const inputFile : string = 'test-data/NoCookies/NoCookiesFailingTestInput.ts'; - TestHelper.assertViolations( + TestHelper.assertViolationsWithTypeChecker( ruleName, inputFile, [ @@ -71,7 +58,7 @@ documentLikeAPIFunction().cookie = '...';`; it('should not throw error ', () : void => { const ruleName : string = 'no-cookies'; const inputFile : string = 'test-data/NoCookies/NoCookiesTestInput-error.ts'; - TestHelper.assertViolations( + TestHelper.assertViolationsWithTypeChecker( ruleName, inputFile, [ diff --git a/src/tests/NoHttpStringRuleTests.ts b/src/tests/NoHttpStringRuleTests.ts index 7c81c0734..c5142334f 100644 --- a/src/tests/NoHttpStringRuleTests.ts +++ b/src/tests/NoHttpStringRuleTests.ts @@ -19,6 +19,31 @@ describe('noHttpStringRule', (): void => { ]); }); + it('should ban http template strings in variables', (): void => { + const inputScript: string = 'var x = `http://www.examples.com`'; + TestHelper.assertViolations(ruleName, inputScript, [ + { + "failure": "Forbidden http url in string: 'http://www.examples.com'", + "name": "file.ts", + "ruleName": ruleName, + "startPosition": {"character": 9, "line": 1} + } + ]); + }); + + it('should ban http multipart template strings in variables', (): void => { + // tslint:disable-next-line:no-invalid-template-strings + const inputScript: string = 'var x = `http://www.${example}.com`'; + TestHelper.assertViolations(ruleName, inputScript, [ + { + "failure": "Forbidden http url in string: 'http://www.'", + "name": "file.ts", + "ruleName": ruleName, + "startPosition": {"character": 9, "line": 1} + } + ]); + }); + it('should ban http strings in default values', (): void => { const inputScript: string = 'function f(x : string = \'http://www.example.com/whatever\') {}'; TestHelper.assertViolations(ruleName, inputScript, [ diff --git a/src/tests/NoSingleLineBlockCommentRuleTests.ts b/src/tests/NoSingleLineBlockCommentRuleTests.ts index 326b15857..204e1d8b9 100644 --- a/src/tests/NoSingleLineBlockCommentRuleTests.ts +++ b/src/tests/NoSingleLineBlockCommentRuleTests.ts @@ -18,14 +18,6 @@ describe('noSingleLineBlockCommentRule', () : void => { TestHelper.assertViolations(ruleName, script, [ ]); }); - it('should pass on a nested single line comment', () : void => { - const script : string = ` - const something = /* my comment */ 'whatever'; - `; - - TestHelper.assertViolations(ruleName, script, [ ]); - }); - it('should pass on tslint suppressions', () : void => { const script : string = ` /* tslint:disable:function-name */ diff --git a/src/tests/NoSparseArraysRuleTests.ts b/src/tests/NoSparseArraysRuleTests.ts deleted file mode 100644 index 573002bbc..000000000 --- a/src/tests/NoSparseArraysRuleTests.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {TestHelper} from './TestHelper'; - -/** - * Unit tests. - */ -describe('noSparseArraysRule', () : void => { - const ruleName : string = 'no-sparse-arrays'; - - it('should pass on dense arrays', () : void => { - const script : string = ` - var a = []; - var b = [1]; - var c = ['1', '2']; - var d = [true, false, true]; - var e = [1,2,3,]; // dangling comma is not an issue - `; - - TestHelper.assertViolations(ruleName, script, [ ]); - }); - - it('should fail on comma with no elements', () : void => { - const script : string = ` - var x = [,]; - `; - - TestHelper.assertViolations(ruleName, script, [ - { - "failure": "Unexpected comma in middle of array", - "name": "file.ts", - "ruleName": "no-sparse-arrays", - "startPosition": { "character": 21, "line": 2 } - } - ]); - }); - - it('should fail on array with many commas', () : void => { - const script : string = ` - var x = [,,,]; - `; - - TestHelper.assertViolations(ruleName, script, [ - { - "failure": "Unexpected comma in middle of array", - "name": "file.ts", - "ruleName": "no-sparse-arrays", - "startPosition": { "character": 21, "line": 2 } - } - ]); - }); - - it('should fail on array with elements and commas', () : void => { - const script : string = ` - var x = [,1,2,3]; - var z = [1,,2,3]; - `; - - TestHelper.assertViolations(ruleName, script, [ - { - "failure": "Unexpected comma in middle of array", - "name": "file.ts", - "ruleName": "no-sparse-arrays", - "startPosition": { "character": 21, "line": 2 } - }, - { - "failure": "Unexpected comma in middle of array", - "name": "file.ts", - "ruleName": "no-sparse-arrays", - "startPosition": { "character": 21, "line": 3 } - } - ]); - }); - -}); \ No newline at end of file diff --git a/src/tests/NoStringBasedSetImmediateTests.ts b/src/tests/NoStringBasedSetImmediateTests.ts index 79392f534..f269a7159 100644 --- a/src/tests/NoStringBasedSetImmediateTests.ts +++ b/src/tests/NoStringBasedSetImmediateTests.ts @@ -9,168 +9,112 @@ describe('noStringBasedSetImmediateRule', () : void => { const RULE_NAME : string = 'no-string-based-set-immediate'; it('should produce violations ', () : void => { - const inputFile : string = ` -var typedStringVariable = 'string variable'; -var functionVariable = () => {}; -var anyVariable : any = () => {}; -var createFunction : () => (() => void) = () => {}; // function that produces a function -var untypedCreateFunction: () => any = () => {}; // function that produces a function -var stringFunction : () => string = () => { return ''; }; // function that produces a string - -// lambdas are OK -setImmediate(() => {}); -this.setImmediate(() => {}); -window.setImmediate(() => {}); -// functions are OK -setImmediate(function () {}); -this.setImmediate(function () {}); -window.setImmediate(function () {}); -// expressions of type function are OK -setImmediate(functionVariable); -this.setImmediate(functionVariable); -window.setImmediate(functionVariable); -var a = setImmediate(functionVariable); -var b = this.setImmediate(functionVariable); -var c = window.setImmediate(functionVariable); -setImmediate(createFunction()); -this.setImmediate(createFunction()); -window.setImmediate(createFunction()); - - - -// this is no longer a false positive -function invoke(functionArg : () => void) { - setImmediate(functionArg); -} - - -// these should all create violations -setImmediate("var x = 'should fail'"); // example 1 -setImmediate(typedStringVariable); // example 2 -setImmediate(anyVariable); // example 3 -setImmediate(untypedCreateFunction()); // example 4 -setImmediate(stringFunction()); // example 5 -this.setImmediate("var x = 'should fail'"); // example 6 -this.setImmediate(typedStringVariable); // example 7 -this.setImmediate(anyVariable); // example 8 -this.setImmediate(untypedCreateFunction()); // example 9 -this.setImmediate(stringFunction()); // example 10 -window.setImmediate("var x = 'should fail'"); // example 11 -window.setImmediate(typedStringVariable); // example 12 -window.setImmediate(anyVariable); // example 13 -window.setImmediate(untypedCreateFunction()); // example 14 -window.setImmediate(stringFunction()); // example 15 -function invoke2(stringArg : string) { - setImmediate(stringArg); // example 16 -} -function invoke3(anyArg : any) { - setImmediate(anyArg); // example 17 -} -`; + const inputFile : string = 'test-data/NoStringBasedSetImmediateTestInput.ts'; TestHelper.assertViolations(RULE_NAME, inputFile, [ { "failure": "Forbidden setImmediate string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 37, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 38, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 39, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 40, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 41, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 42, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 43, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 44, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 45, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 46, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 47, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 48, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 49, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 50, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 51, "character": 1 } }, { "failure": "Forbidden setImmediate string parameter: stringArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 53, "character": 5 } }, { "failure": "Forbidden setImmediate string parameter: anyArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-immediate", "startPosition": { "line": 56, "character": 5 } } - ]); + ], + true); }); }); /* tslint:enable:max-func-body-length */ diff --git a/src/tests/NoStringBasedSetIntervalTests.ts b/src/tests/NoStringBasedSetIntervalTests.ts index d0500838c..f568708bd 100644 --- a/src/tests/NoStringBasedSetIntervalTests.ts +++ b/src/tests/NoStringBasedSetIntervalTests.ts @@ -9,164 +9,107 @@ describe('noStringBasedSetIntervalRule', () : void => { const RULE_NAME : string = 'no-string-based-set-interval'; it('should produce violations ', () : void => { - const inputFile : string = ` -var typedStringVariable = 'string variable'; -var functionVariable = () => {}; -var anyVariable : any = () => {}; -var createFunction : () => (() => void) = () => {}; // function that produces a function -var untypedCreateFunction: () => any = () => {}; // function that produces a function -var stringFunction : () => string = () => { return ''; }; // function that produces a string - -// lambdas are OK -setInterval(() => {}); -this.setInterval(() => {}); -window.setInterval(() => {}); -// functions are OK -setInterval(function () {}); -this.setInterval(function () {}); -window.setInterval(function () {}); -// expressions of type function are OK -setInterval(functionVariable); -this.setInterval(functionVariable); -window.setInterval(functionVariable); -var a = setInterval(functionVariable); -var b = this.setInterval(functionVariable); -var c = window.setInterval(functionVariable); -setInterval(createFunction()); -this.setInterval(createFunction()); -window.setInterval(createFunction()); - - - -// this used to be a false positive. -function invoke(functionArg : () => void) { - setInterval(functionArg); -} - - -// these should all create violations -setInterval("var x = 'should fail'"); // example 1 -setInterval(typedStringVariable); // example 2 -setInterval(anyVariable); // example 3 -setInterval(untypedCreateFunction()); // example 4 -setInterval(stringFunction()); // example 5 -this.setInterval("var x = 'should fail'"); // example 6 -this.setInterval(typedStringVariable); // example 7 -this.setInterval(anyVariable); // example 8 -this.setInterval(untypedCreateFunction()); // example 9 -this.setInterval(stringFunction()); // example 10 -window.setInterval("var x = 'should fail'"); // example 11 -window.setInterval(typedStringVariable); // example 12 -window.setInterval(anyVariable); // example 13 -window.setInterval(untypedCreateFunction()); // example 14 -window.setInterval(stringFunction()); // example 15 -function invoke2(stringArg : string) { - setInterval(stringArg); // example 16 -} -function invoke3(anyArg : any) { - setInterval(anyArg); // example 17 -} -`; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + const inputFile : string = 'test-data/NoStringBasedSetIntervalTestInput.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setInterval string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 37, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 38, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 39, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 40, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 41, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 42, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 43, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 44, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 45, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 46, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: \"var x = 'should fail'\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 47, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: typedStringVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 48, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: anyVariable", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 49, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: untypedCreateFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 50, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: stringFunction()", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 51, "character": 1 } }, { "failure": "Forbidden setInterval string parameter: stringArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 53, "character": 5 } }, { "failure": "Forbidden setInterval string parameter: anyArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-interval", "startPosition": { "line": 56, "character": 5 } } diff --git a/src/tests/NoStringBasedSetTimeoutTests.ts b/src/tests/NoStringBasedSetTimeoutTests.ts index 20b79570f..11cc8ada7 100644 --- a/src/tests/NoStringBasedSetTimeoutTests.ts +++ b/src/tests/NoStringBasedSetTimeoutTests.ts @@ -8,207 +8,85 @@ describe('noStringBasedSetTimeoutRule', () : void => { it('should not throw error - case 1', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-error.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, []); + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should not throw error - case 2', () : void => { - const inputFile : string = ` -var globalProp1: () => void; -var globalProp2 = () => {}; -var globalProp3 = function () {}; - -function globalFunction1() {} - -setTimeout(globalProp1, 5); -setTimeout(globalProp2, 5); -setTimeout(globalProp3, 5); -setTimeout(globalFunction1, 5); - -module SetTimeoutModule { - - var moduleProp1: () => void; - var moduleProp2 = () => {}; - var moduleProp3 = function () {}; - - function moduleFunction1() { - setTimeout(moduleProp1, 5); - setTimeout(moduleProp2, 5); - setTimeout(moduleProp3, 5); - setTimeout(moduleFunction1, 5); - } - - class View { - - private property1: () => void; - private property2 = () => {}; - private property3 = function () {}; - - private static property4: () => void; - private static property5 = () => {}; - private static property6 = function () {}; - - private static method1() {} - - private method2(): void { - setTimeout(View.method1, 5); - setTimeout(View.property4, 5); - setTimeout(View.property5, 5); - setTimeout(View.property6, 5); - - setTimeout(this.method2, 5); - setTimeout(this.property1, 5); - setTimeout(this.property2, 5); - - setTimeout(globalProp1, 5); - setTimeout(globalProp2, 5); - setTimeout(globalProp3, 5); - setTimeout(globalFunction1, 5); - - setTimeout(moduleProp1, 5); - setTimeout(moduleProp2, 5); - setTimeout(moduleProp3, 5); - setTimeout(moduleFunction1, 5); - - setTimeout(this.property1, 5); - - var f = function() { - setTimeout(this.method2, 5); - setTimeout(this.property1, 5); - setTimeout(this.property2, 5); - setTimeout(this.property3, 5); - }; - } - } -} -`; - TestHelper.assertViolations(RULE_NAME, inputFile, []); + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case2.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should pass when function parameter is Function', () : void => { - const script : string = ` - function(callback: Function) { - setTimeout(callback, 0); - }`; - TestHelper.assertViolations(RULE_NAME, script, []); + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-functionParamFunction.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should support type inference on shadowed variables', () : void => { - const inputFile : string = ` -var globalProp1: () => void; -var globalProp2 = () => {}; -var globalProp3 = function () {}; - -function globalFunction1() {} - -module SetTimeoutModule { - - var moduleProp1: () => void; - var moduleProp2 = () => {}; - var moduleProp3 = function () {}; - - var globalProp1: any; // ! shadow ! - var globalFunction1 = 'not a function'; // ! shadow ! - - function moduleFunction1(globalProp2: any, moduleProp1: any) { // ! shadow ! - setTimeout(moduleProp1, 5); // should fail - setTimeout(moduleProp2, 5); // should pass - setTimeout(globalFunction1, 5); // should fail - setTimeout(globalProp2, 5); // should fail - } - - class View { - - constructor(moduleProp1, globalProp3) { - setTimeout(globalProp1, 5); // should fail - setTimeout(moduleProp1, 5); // should fail - setTimeout(globalProp2, 5); // should pass - } - - private method2(globalProp3: any): void { // ! shadow ! - var f = function(moduleProp1) { // ! shadow ! - var x = (moduleProp2) => { - setTimeout(globalProp1, 5); // should fail - setTimeout(globalProp2, 5); // should pass - setTimeout(globalProp3, 5); // should fail - setTimeout(globalFunction1, 5); // should fail - - setTimeout(moduleProp1, 5); // should fail - setTimeout(moduleProp2, 5); // should fail - setTimeout(moduleProp3, 5); // should pass - } - }; - } - - public set someSetter(moduleProp1: string) { - setTimeout(moduleProp1, 5); // should fail - } - } -} -`; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-shadowedVariables.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: moduleProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 18 } }, { "failure": "Forbidden setTimeout string parameter: globalFunction1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 20 } }, { "failure": "Forbidden setTimeout string parameter: globalProp2", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 21 } }, { "failure": "Forbidden setTimeout string parameter: globalProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 13, "line": 27 } }, { "failure": "Forbidden setTimeout string parameter: moduleProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 13, "line": 28 } }, { "failure": "Forbidden setTimeout string parameter: globalProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 21, "line": 35 } }, { "failure": "Forbidden setTimeout string parameter: globalProp3", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 21, "line": 37 } }, { "failure": "Forbidden setTimeout string parameter: globalFunction1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 21, "line": 38 } }, { "failure": "Forbidden setTimeout string parameter: moduleProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 21, "line": 40 } }, { "failure": "Forbidden setTimeout string parameter: moduleProp2", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 21, "line": 41 } }, { "failure": "Forbidden setTimeout string parameter: moduleProp1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 13, "line": 48 } } @@ -217,12 +95,12 @@ module SetTimeoutModule { it('should not throw error - case 3', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-error3.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, []); + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should not throw error - case 4', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-error4.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: this.onAnimationEnd()", "name": "test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-error4.ts", @@ -234,39 +112,17 @@ module SetTimeoutModule { it('should not throw error - case 5', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-error5.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, []); + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should not produce violations', () : void => { - const inputFile : string = ` -var functionVariable = () => {}; -var createFunction : () => (() => void) = () => {}; // function that produces a function - -// lambdas are OK -setTimeout(() => {}); -this.setTimeout(() => {}); -window.setTimeout(() => {}); -// functions are OK -setTimeout(function () {}); -this.setTimeout(function () {}); -window.setTimeout(function () {}); -// expressions of type function are OK -setTimeout(functionVariable); -this.setTimeout(functionVariable); -window.setTimeout(functionVariable); -var a = setTimeout(functionVariable); -var b = this.setTimeout(functionVariable); -var c = window.setTimeout(functionVariable); -setTimeout(createFunction()); -this.setTimeout(createFunction()); -window.setTimeout(createFunction()); -`; - TestHelper.assertViolations(RULE_NAME, inputFile, []); + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case3.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, []); }); it('should produce violations for string literals', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-string-literals.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "ruleName": "no-string-based-set-timeout", "failure": "Forbidden setTimeout string parameter: \"var x = 'should fail'\"", @@ -290,7 +146,7 @@ window.setTimeout(createFunction()); it('should produce violations for string variables', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-string-variables.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "ruleName": "no-string-based-set-timeout", "failure": "Forbidden setTimeout string parameter: typedStringVariable", @@ -315,7 +171,7 @@ window.setTimeout(createFunction()); it('should produce violations for any variables', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-any-variables.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "ruleName": "no-string-based-set-timeout", "name": "test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-any-variables.ts", @@ -339,7 +195,7 @@ window.setTimeout(createFunction()); it('should produce violations for any functions', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-any-functions.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: untypedCreateFunction()", "name": "test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-any-functions.ts", @@ -363,7 +219,7 @@ window.setTimeout(createFunction()); it('should produce violations for string functions', () : void => { const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-string-functions.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: stringFunction()", "name": "test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-string-functions.ts", @@ -386,25 +242,17 @@ window.setTimeout(createFunction()); }); it('should produce violations for parameters', () : void => { - const inputFile : string = ` -// these should all create violations -function invoke2(stringArg : string) { - setTimeout(stringArg); // example 16 -} -function invoke3(anyArg : any) { - setTimeout(anyArg); // example 17 -} -`; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-parameters.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: stringArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "line": 4, "character": 5 } }, { "failure": "Forbidden setTimeout string parameter: anyArg", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "line": 7, "character": 5 } } @@ -412,41 +260,30 @@ function invoke3(anyArg : any) { }); it('should not produce violations what used to be a false positive case', () : void => { - const inputFile : string = ` - function invoke(functionArg1 : () => void, functionArg2 = () => {}) { - setTimeout(functionArg1); - setTimeout(functionArg2); - }`; - - TestHelper.assertViolations(RULE_NAME, inputFile, [ ]); + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-formerFalsePositive.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ ]); }); it('should not fail within a constructor', () : void => { - const inputFile : string = ` - class MyClass { - constructor(arg1) { - setTimeout(arg1, 5); - } - }`; - - TestHelper.assertViolations(RULE_NAME, inputFile, [ + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-constructor.ts'; + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: arg1", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", - "startPosition": { "character": 17, "line": 4 } + "startPosition": { "character": 9, "line": 4 } } ]); }); it('should create violations on template strings', () : void => { - const inputFile : string = `var data = 'alert(1)'; -$window.setTimeout(\`\${data}\`, 200);`; + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-template-strings.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { + // tslint:disable-next-line:no-invalid-template-strings "failure": "Forbidden setTimeout string parameter: `${data}`", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 1, "line": 2 } } @@ -454,51 +291,32 @@ $window.setTimeout(\`\${data}\`, 200);`; }); it('should pass all Issue #46 usages', () : void => { - const inputFile : string = `class TestClassIssue46 { - - constructor(private $window: angular.IWindowService) { - - //Arrow Functions - setTimeout(() => { }, 100); - $window.setTimeout(() => { }, 100); - - //Standard Functions - setTimeout(function () { }, 100); - $window.setTimeout(function () { }, 100); - - //Strings - const alertNum = 1; - setTimeout("alert(" + alertNum + ")", 100); - $window.setTimeout("alert(" + alertNum + ")", 100); - - //TS Template Strings - setTimeout(\`alert(\${alertNum})\`, 100); - $window.setTimeout(\`alert(\${alertNum})\`, 100); - } -}`; + const inputFile : string = 'test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-issue46.ts'; - TestHelper.assertViolations(RULE_NAME, inputFile, [ + TestHelper.assertViolationsWithTypeChecker(RULE_NAME, inputFile, [ { "failure": "Forbidden setTimeout string parameter: \"alert(\" + alertNum + \")\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 15 } }, { "failure": "Forbidden setTimeout string parameter: \"alert(\" + alertNum + \")\"", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 16 } }, { + // tslint:disable-next-line:no-invalid-template-strings "failure": "Forbidden setTimeout string parameter: `alert(${alertNum})`", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 19 } }, { + // tslint:disable-next-line:no-invalid-template-strings "failure": "Forbidden setTimeout string parameter: `alert(${alertNum})`", - "name": "file.ts", + "name": inputFile, "ruleName": "no-string-based-set-timeout", "startPosition": { "character": 9, "line": 20 } } diff --git a/src/tests/NoUnexternalizedStringsRuleTests.ts b/src/tests/NoUnexternalizedStringsRuleTests.ts index 76b16d61f..744b97a43 100644 --- a/src/tests/NoUnexternalizedStringsRuleTests.ts +++ b/src/tests/NoUnexternalizedStringsRuleTests.ts @@ -14,6 +14,7 @@ describe('noUnexternalizedStringsRule', () : void => { }); it('should pass on template expression', () : void => { + // tslint:disable-next-line:no-invalid-template-strings const script : string = 'let str = `Hello ${var} Worlds`;'; TestHelper.assertViolationsWithOptions(ruleName, [{ signatures: ['localize', 'nls.localize'], messageIndex: 1 }], script, [ ]); }); diff --git a/src/tests/NoUnsupportedBrowserCodeRuleTests.ts b/src/tests/NoUnsupportedBrowserCodeRuleTests.ts index d3b99c237..6a0294625 100644 --- a/src/tests/NoUnsupportedBrowserCodeRuleTests.ts +++ b/src/tests/NoUnsupportedBrowserCodeRuleTests.ts @@ -120,7 +120,7 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, + "character": 17, "line": 3 } }, @@ -129,7 +129,7 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, + "character": 17, "line": 8 } }, @@ -138,8 +138,8 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, - "line": 14 + "character": 17, + "line": 13 } }, { @@ -147,7 +147,7 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, + "character": 17, "line": 20 } }, @@ -156,8 +156,8 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, - "line": 28 + "character": 17, + "line": 25 } }, { @@ -165,8 +165,8 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, - "line": 29 + "character": 17, + "line": 25 } }, { @@ -174,8 +174,8 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, - "line": 30 + "character": 17, + "line": 25 } }, { @@ -183,8 +183,8 @@ describe('noUnsupportedBrowserCodeRule', () : void => { "name": "file.ts", "ruleName": "no-unsupported-browser-code", "startPosition": { - "character": 20, - "line": 39 + "character": 17, + "line": 36 } } ]); diff --git a/src/tests/NoUnusedImportsTests.ts b/src/tests/NoUnusedImportsTests.ts deleted file mode 100644 index ec72fd93a..000000000 --- a/src/tests/NoUnusedImportsTests.ts +++ /dev/null @@ -1,347 +0,0 @@ -import {TestHelper} from './TestHelper'; - -/** - * Unit tests. - */ -describe('noUnusedImportsRule', () : void => { - - const ruleName : string = 'no-unused-imports'; - - it('should pass on require import', () : void => { - const inputFile : string = ` - import chai = require('chai') - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, []); - }); - - it('should pass on ES6 star import', () : void => { - const inputFile : string = ` - import * as chai2 from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai2); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, []); - }); - - it('should pass on ES6 import', () : void => { - const inputFile : string = ` - import chai3 from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai3); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, []); - }); - - it('should pass on ES6 braced import', () : void => { - const inputFile : string = ` - import { chai4 } from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai4); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, []); - }); - - it('should pass on ES6 braced multi-import', () : void => { - const inputFile : string = ` - import { chai5, chai6 } from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai5); - console.log(chai6); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, []); - }); - - it('should fail on unused require import', () : void => { - const inputFile : string = ` - import NoUnusedImportsRule = require('../src/noUnusedImportsRule'); - - class NoUnusedImportsTestInput { - constructor() { - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'NoUnusedImportsRule'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 20 } - } - ]); - }); - - it('should be able to handle React imports in tsx files', () : void => { - const inputFile : string = `import React = require('react'); -import BaseComponent = require('common/component/BaseComponent'); -import I18nFacade = require('common/i18n/I18nFacade'); - -export class AuthorSummary extends BaseComponent.BaseComponent { - - public render() { - return
- 'some text' -
; - } -} -`; - TestHelper.assertViolations( - ruleName, - inputFile, - [] - ); - }); - - it('should be able to handle React ES6 imports in tsx files', () : void => { - const inputFile : string = `import React from 'React'; -import BaseComponent from 'common/component/BaseComponent'; - -export class AuthorSummary extends BaseComponent.BaseComponent { - - public render() { - return
- 'some text' -
; - } -} -`; - TestHelper.assertViolations( - ruleName, - inputFile, - [] - ); - }); - - it('should be able to handle import static references in tsx files', () : void => { - const inputFile : string = `import React = require('react'); -import BaseComponent = require('common/component/BaseComponent'); -import I18nFacade = require('common/i18n/I18nFacade'); - -export class AuthorSummary extends BaseComponent.BaseComponent { - - public render() { - return
- 'some text' -
; - } -} -`; - TestHelper.assertViolations( - ruleName, - inputFile, - [] - ); - }); - - it('should flag an unused relative import', () : void => { - const inputScript : string = ` -import DM = require("DM"); -import AB = DM.Dependency; -console.log(DM);`; // AB import is not used! - - TestHelper.assertViolations(ruleName, inputScript, [ - { - "failure": "unused import: 'AB'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 3, "character": 8 } - } - ]); - }); - - it('should flag an unused relative ES6 import', () : void => { - const inputScript : string = ` -import DM from "DM"; -import AB as DM.Dependency; -console.log(DM);`; // AB import is not used! - - TestHelper.assertViolations(ruleName, inputScript, [ - { - "failure": "unused import: 'AB'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 3, "character": 8 } - } - ]); - }); - - it('should not flag imports that are used as other imports', () : void => { - const inputScript : string = ` -import DM = require("DM"); -import AB = DM.Dependency; -console.log(AB);`; - - TestHelper.assertViolations(ruleName, inputScript, []); - }); - - it('should not flag imports that are used as other ES6 imports', () : void => { - const inputScript : string = ` -import DM as "DM"; -import AB as DM.Dependency; -console.log(AB);`; - - TestHelper.assertViolations(ruleName, inputScript, []); - }); - - it('should pass on dot-import (from MSE)', (): void => { - const inputScript: string = ` - import React = require('react'); - import Simulate = React.addons.TestUtils.Simulate; - Simulate.doit(); - `; - - TestHelper.assertViolations( ruleName, inputScript, []); - }); - - it('should fail on ES6 star import', () : void => { - const inputFile : string = ` - import * as chai2 from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'chai2'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 25 } - } - ]); - }); - - it('should fail on ES6 import', () : void => { - const inputFile : string = ` - import chai3 from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'chai3'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 20 } - } - ]); - }); - - it('should fail on ES6 braced import', () : void => { - const inputFile : string = ` - import { chai4 } from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'chai4'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 22 } - } - ]); - }); - - it('should fail on ES6 braced multi-import', () : void => { - const inputFile : string = ` - import { chai5, chai6 } from 'chai' - - class NoUnusedImportsTestInput { - constructor() { - console.log(chai5); - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'chai6'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 29 } - } - ]); - }); - - it('should fail on unused require import', () : void => { - const inputFile : string = ` - import NoUnusedImportsRule = require('../src/noUnusedImportsRule'); - - class NoUnusedImportsTestInput { - constructor() { - } - } - `; - TestHelper.assertViolations( ruleName, inputFile, [ - { - "failure": "unused import: 'NoUnusedImportsRule'", - "name": "file.ts", - "ruleName": "no-unused-imports", - "startPosition": { "line": 2, "character": 20 } - } - ]); - }); - - it('should fail on missing reference in tsx files', () : void => { - const inputFile : string = `import React = require('react'); -import BaseComponent = require('common/component/BaseComponent'); -import I18nFacade = require('common/i18n/I18nFacade'); - -export class AuthorSummary extends BaseComponent.BaseComponent { - - public render() { - return
- 'some text' -
; - } -} -`; - TestHelper.assertViolations( - ruleName, - inputFile, - [ - { - "failure": "unused import: 'I18nFacade'", - "name": "file.tsx", - "ruleName": "no-unused-imports", - "startPosition": { "character": 8, "line": 3 } - } - ] - ); - }); - -}); \ No newline at end of file diff --git a/src/tests/NoUselessFilesRuleTests.ts b/src/tests/NoUselessFilesRuleTests.ts index 02f3b47e6..e03617638 100644 --- a/src/tests/NoUselessFilesRuleTests.ts +++ b/src/tests/NoUselessFilesRuleTests.ts @@ -54,9 +54,9 @@ describe('noUselessFilesRule', () : void => { } } */ - + // here is a single line comment - + /* and another multline comment */`; diff --git a/src/tests/TestHelper.ts b/src/tests/TestHelper.ts index 9154fa428..3df3f61ae 100644 --- a/src/tests/TestHelper.ts +++ b/src/tests/TestHelper.ts @@ -1,6 +1,7 @@ import * as Lint from 'tslint'; import * as fs from 'fs'; import * as chai from 'chai'; +import * as ts from 'typescript'; import {ErrorTolerantWalker} from '../utils/ErrorTolerantWalker'; /** @@ -8,6 +9,8 @@ import {ErrorTolerantWalker} from '../utils/ErrorTolerantWalker'; */ export module TestHelper { + let program: ts.Program; + /* tslint:disable:prefer-const */ /** * This setting must point to your rule .js files. 3rd party libraries may reuse this class and change value. @@ -30,49 +33,75 @@ export module TestHelper { line: number; position?: number; } + export interface Fix { + innerStart: number; + innerLength: number; + innerText: string; + } export interface ExpectedFailure { ruleName: string; name: string; - failure: string; + failure?: string; + ruleSeverity?: string; endPosition?: FailurePosition; startPosition: FailurePosition; - } - interface RuleConfiguration { - rules: { - [key: string]: boolean | any[]; - }; + fix?: Fix; } export function assertNoViolation(ruleName: string, - inputFileOrScript: string) { - runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, []); + inputFileOrScript: string, + useTypeChecker: boolean = false) { + runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, [], useTypeChecker); } export function assertNoViolationWithOptions(ruleName: string, options: any[], - inputFileOrScript: string) { - runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, []); + inputFileOrScript: string, + useTypeChecker: boolean = false) { + runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, [], useTypeChecker); } export function assertViolationsWithOptions(ruleName: string, options: any[], inputFileOrScript: string, - expectedFailures: ExpectedFailure[]) { - runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, expectedFailures); + expectedFailures: ExpectedFailure[], + useTypeChecker: boolean = false) { + runRuleAndEnforceAssertions(ruleName, options, inputFileOrScript, expectedFailures, useTypeChecker); } export function assertViolations(ruleName: string, + inputFileOrScript: string, + expectedFailures: ExpectedFailure[], + useTypeChecker: boolean = false) { + runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, expectedFailures, useTypeChecker); + } + export function assertViolationsWithTypeChecker(ruleName: string, inputFileOrScript: string, expectedFailures: ExpectedFailure[]) { - runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, expectedFailures); + runRuleAndEnforceAssertions(ruleName, null, inputFileOrScript, expectedFailures, true); } - export function runRule(ruleName : string, userOptions: string[], inputFileOrScript : string): Lint.LintResult { - const configuration: RuleConfiguration = { - rules: {} + export function runRule( + ruleName : string, + userOptions: string[], + inputFileOrScript : string, + useTypeChecker : boolean = false): Lint.LintResult { + + const configuration: Lint.Configuration.IConfigurationFile = { + extends: [], + jsRules: new Map>(), + linterOptions: {}, + rules: new Map>(), + rulesDirectory: [] }; + if (userOptions != null && userOptions.length > 0) { //options like `[4, 'something', false]` were passed, so prepend `true` to make the array like `[true, 4, 'something', false]` - configuration.rules[ruleName] = ([true]).concat(userOptions); + configuration.rules.set(ruleName, { + ruleName, + ruleArguments: userOptions + }); } else { - configuration.rules[ruleName] = true; + configuration.rules.set(ruleName, { + ruleName + }); } const options : Lint.ILinterOptions = { @@ -85,9 +114,13 @@ export module TestHelper { const debug: boolean = ErrorTolerantWalker.DEBUG; ErrorTolerantWalker.DEBUG = true; // never fail silently let result: Lint.LintResult; + if (useTypeChecker) { + //program = Lint.Linter.createProgram([ ], './dist/test-data'); + program = ts.createProgram([inputFileOrScript], { }); + } if (inputFileOrScript.match(/.*\.ts(x)?$/)) { const contents = fs.readFileSync(inputFileOrScript, FILE_ENCODING); - const linter = new Lint.Linter(options); + const linter = new Lint.Linter(options, useTypeChecker ? program : undefined); linter.lint(inputFileOrScript, contents, configuration); result = linter.getResult(); } else { @@ -97,7 +130,8 @@ export module TestHelper { } else { filename = 'file.ts'; } - const linter = new Lint.Linter(options); + + const linter = new Lint.Linter(options, useTypeChecker ? program : undefined); linter.lint(filename, inputFileOrScript, configuration); result = linter.getResult(); } @@ -106,9 +140,9 @@ export module TestHelper { } function runRuleAndEnforceAssertions(ruleName : string, userOptions: string[], inputFileOrScript : string, - expectedFailures : ExpectedFailure[]) { + expectedFailures : ExpectedFailure[], useTypeChecker: boolean = false) { - const lintResult: Lint.LintResult = runRule(ruleName, userOptions, inputFileOrScript); + const lintResult: Lint.LintResult = runRule(ruleName, userOptions, inputFileOrScript, useTypeChecker); const actualFailures: ExpectedFailure[] = JSON.parse(lintResult.output); // All the information we need is line and character of start position. For JSON comparison @@ -125,6 +159,9 @@ export module TestHelper { expectedFailures.forEach((expected: ExpectedFailure): void => { delete expected.startPosition.position; delete expected.endPosition; + if (!expected.ruleSeverity) { + expected.ruleSeverity = 'ERROR'; + } }); const errorMessage = `Wrong # of failures: \n${JSON.stringify(actualFailures, null, 2)}`; diff --git a/src/tests/UseIsnanRuleTests.ts b/src/tests/UseIsnanRuleTests.ts deleted file mode 100644 index 3d38ce5a2..000000000 --- a/src/tests/UseIsnanRuleTests.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {TestHelper} from './TestHelper'; - -/** - * Unit tests. - */ -describe('useIsnanRule', () : void => { - const ruleName : string = 'use-isnan'; - - it('should pass on xxx', () : void => { - const script : string = ` - if (isNaN(NaN)) { } - if (isNaN(something)) { } - `; - - TestHelper.assertViolations(ruleName, script, [ ]); - }); - - it('should fail on equality NaN', () : void => { - const script : string = ` - if (foo == NaN) { } - if (NaN === foo) { } - if (foo != NaN) { } - if (NaN !== foo) { } - `; - - TestHelper.assertViolations(ruleName, script, [ - { - "failure": "Found an invalid comparison for NaN: foo == NaN", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 2 } - }, - { - "failure": "Found an invalid comparison for NaN: NaN === foo", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 3 } - }, - { - "failure": "Found an invalid comparison for NaN: foo != NaN", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 4 } - }, - { - "failure": "Found an invalid comparison for NaN: NaN !== foo", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 5 } - } - ]); - }); - - it('should fail on other binary expressions', () : void => { - const script : string = ` - if (foo > NaN) { } - if (NaN >= foo) { } - if (foo < NaN) { } - if (NaN <= foo) { } - `; - - TestHelper.assertViolations(ruleName, script, [ - { - "failure": "Found an invalid comparison for NaN: foo > NaN", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 2 } - }, - { - "failure": "Found an invalid comparison for NaN: NaN >= foo", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 3 } - }, - { - "failure": "Found an invalid comparison for NaN: foo < NaN", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 4 } - }, - { - "failure": "Found an invalid comparison for NaN: NaN <= foo", - "name": "file.ts", - "ruleName": "use-isnan", - "startPosition": { "character": 13, "line": 5 } - } - ]); - }); -}); \ No newline at end of file diff --git a/src/underscoreConsistentInvocationRule.ts b/src/underscoreConsistentInvocationRule.ts index 89fb43b05..cac8a81dc 100644 --- a/src/underscoreConsistentInvocationRule.ts +++ b/src/underscoreConsistentInvocationRule.ts @@ -70,10 +70,10 @@ class UnderscoreConsistentInvocationRuleWalker extends ErrorTolerantWalker { const functionName: string = AstUtils.getFunctionName(node); if (this.style === 'instance' && this.isStaticUnderscoreInvocation(node)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_STATIC_FOUND + '_.' + functionName)); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_STATIC_FOUND + '_.' + functionName); } if (this.style === 'static' && this.isStaticUnderscoreInstanceInvocation(node)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), FAILURE_INSTANCE_FOUND + node.expression.getText())); + this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_INSTANCE_FOUND + node.expression.getText()); } super.visitCallExpression(node); } diff --git a/src/useIsnanRule.ts b/src/useIsnanRule.ts deleted file mode 100644 index 55ed3152d..000000000 --- a/src/useIsnanRule.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as ts from 'typescript'; -import * as Lint from 'tslint'; - -import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; -import {ExtendedMetadata} from './utils/ExtendedMetadata'; - -/** - * Implementation of the use-isnan rule. - */ -export class Rule extends Lint.Rules.AbstractRule { - - public static metadata: ExtendedMetadata = { - ruleName: 'use-isnan', - type: 'maintainability', - description: 'enforces that you use the isNaN() function to check for NaN references instead of a comparison to the NaN constant.', - options: null, - optionsDescription: '', - typescriptOnly: true, - issueClass: 'Ignored', - issueType: 'Error', - severity: 'Critical', - level: 'Opportunity for Excellence', - group: 'Ignored' - }; - - public static FAILURE_STRING: string = 'Found an invalid comparison for NaN: '; - - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new UseIsnanRuleWalker(sourceFile, this.getOptions())); - } -} - -class UseIsnanRuleWalker extends ErrorTolerantWalker { - protected visitBinaryExpression(node: ts.BinaryExpression): void { - if (this.isExpressionNaN(node.left) || this.isExpressionNaN(node.right)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING + node.getText())); - } - super.visitBinaryExpression(node); - } - - private isExpressionNaN(node: ts.Node) { - return node.kind === ts.SyntaxKind.Identifier && node.getText() === 'NaN'; - } -} diff --git a/src/useNamedParameterRule.ts b/src/useNamedParameterRule.ts index b5ecf3bf5..552d02f61 100644 --- a/src/useNamedParameterRule.ts +++ b/src/useNamedParameterRule.ts @@ -37,8 +37,7 @@ class UseNamedParameterWalker extends ErrorTolerantWalker { if (node.argumentExpression.kind === ts.SyntaxKind.NumericLiteral) { if (node.expression.getText() === 'arguments') { const failureString = Rule.FAILURE_STRING + '\'' + node.getText() + '\''; - const failure = this.createFailure(node.getStart(), node.getWidth(), failureString); - this.addFailure(failure); + this.addFailureAt(node.getStart(), node.getWidth(), failureString); } } } diff --git a/src/utils/AstUtils.ts b/src/utils/AstUtils.ts index 495260f30..04a124294 100644 --- a/src/utils/AstUtils.ts +++ b/src/utils/AstUtils.ts @@ -56,19 +56,19 @@ export module AstUtils { || expression.kind === ts.SyntaxKind.PropertyAccessExpression) { const definitionInfo : ts.DefinitionInfo[] = languageServices.getDefinitionAtPosition('file.ts', expression.getStart()); if (definitionInfo) { - definitionInfo.forEach((definitionInfo : ts.DefinitionInfo, index : number) : void => { + definitionInfo.forEach((info : ts.DefinitionInfo, index : number) : void => { console.log('\tdefinitionInfo-' + index); - console.log('\t\tkind: ' + definitionInfo.kind); - console.log('\t\tname: ' + definitionInfo.name); + console.log('\t\tkind: ' + info.kind); + console.log('\t\tname: ' + info.name); }); } const typeInfo : ts.DefinitionInfo[] = languageServices.getTypeDefinitionAtPosition('file.ts', expression.getStart()); if (typeInfo) { - typeInfo.forEach((definitionInfo : ts.DefinitionInfo, index : number) : void => { + typeInfo.forEach((info : ts.DefinitionInfo, index : number) : void => { console.log('\ttypeDefinitionInfo-' + index); - console.log('\t\tkind: ' + definitionInfo.kind); - console.log('\t\tname: ' + definitionInfo.name); + console.log('\t\tkind: ' + info.kind); + console.log('\t\tname: ' + info.name); }); } diff --git a/src/utils/BannedTermWalker.ts b/src/utils/BannedTermWalker.ts index f43ead1d6..3761f82c8 100644 --- a/src/utils/BannedTermWalker.ts +++ b/src/utils/BannedTermWalker.ts @@ -78,7 +78,7 @@ export class BannedTermWalker extends ErrorTolerantWalker { if ((node).name.text) { const text : string = (node).name.text; if (this.isBannedTerm(text)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), this.failureString + text)); + this.addFailureAt(node.getStart(), node.getWidth(), this.failureString + text); } } } diff --git a/src/utils/ErrorTolerantWalker.ts b/src/utils/ErrorTolerantWalker.ts index 1f1f7d073..ad218f161 100644 --- a/src/utils/ErrorTolerantWalker.ts +++ b/src/utils/ErrorTolerantWalker.ts @@ -20,10 +20,10 @@ export class ErrorTolerantWalker extends Lint.RuleWalker { + '\nNode: ' + (node.getFullText ? node.getFullText() : '') + '\n' + e; - this.addFailure(this.createFailure( + this.addFailureAt( node.getStart ? node.getStart() : 0, node.getWidth ? node.getWidth() : 0, - msg)); + msg); } } } diff --git a/src/utils/JsxAttribute.ts b/src/utils/JsxAttribute.ts index 862b3f575..75b594fff 100644 --- a/src/utils/JsxAttribute.ts +++ b/src/utils/JsxAttribute.ts @@ -141,17 +141,17 @@ export function getNumericLiteral(node: ts.JsxAttribute): string { * Get an array of attributes in the given node. * It contains JsxAttribute and JsxSpreadAttribute. */ -export function getAllAttributesFromJsxElement(node: ts.Node): (ts.JsxAttribute | ts.JsxSpreadAttribute)[] { - let attributes: (ts.JsxAttribute | ts.JsxSpreadAttribute)[]; +export function getAllAttributesFromJsxElement(node: ts.Node): ts.NodeArray { + let attributes: ts.NodeArray; if (node == null) { - return []; + return >[]; } else if (isJsxElement(node)) { - attributes = node.openingElement.attributes; + attributes = node.openingElement.attributes.properties; } else if (isJsxSelfClosingElement(node)) { - attributes = node.attributes; + attributes = node.attributes.properties; } else if (isJsxOpeningElement(node)) { - attributes = node.attributes; + attributes = node.attributes.properties; } else { throw new Error('The node must be a JsxElement, JsxSelfClosingElement or JsxOpeningElement.'); } diff --git a/src/utils/NoStringParameterToFunctionCallWalker.ts b/src/utils/NoStringParameterToFunctionCallWalker.ts index 625a82d6d..b030977c4 100644 --- a/src/utils/NoStringParameterToFunctionCallWalker.ts +++ b/src/utils/NoStringParameterToFunctionCallWalker.ts @@ -14,8 +14,8 @@ export class NoStringParameterToFunctionCallWalker extends ScopedSymbolTrackingW public constructor(sourceFile : ts.SourceFile, targetFunctionName : string, options : Lint.IOptions, - languageServices : ts.LanguageService) { - super(sourceFile, options, languageServices); + program? : ts.Program) { + super(sourceFile, options, program); this.targetFunctionName = targetFunctionName; this.failureString = 'Forbidden ' + targetFunctionName + ' string parameter: '; } @@ -31,7 +31,7 @@ export class NoStringParameterToFunctionCallWalker extends ScopedSymbolTrackingW if (functionName === this.targetFunctionName && firstArg != null) { if (!this.isExpressionEvaluatingToFunction(firstArg)) { const msg : string = this.failureString + firstArg.getFullText().trim().substring(0, 40); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), msg)); + this.addFailureAt(node.getStart(), node.getWidth(), msg); } } } diff --git a/src/utils/Scope.ts b/src/utils/Scope.ts index 26fd99683..4ac2d69a1 100644 --- a/src/utils/Scope.ts +++ b/src/utils/Scope.ts @@ -58,10 +58,10 @@ class GlobalReferenceCollector extends ErrorTolerantWalker { public nonFunctionIdentifiers: string[] = []; /* tslint:disable:no-empty */ - protected visitModuleDeclaration(node: ts.ModuleDeclaration): void { } // do not descend into fresh scopes - protected visitClassDeclaration(node: ts.ClassDeclaration): void { } // do not descend into fresh scopes - protected visitArrowFunction(node: ts.ArrowFunction): void { } // do not descend into fresh scopes - protected visitFunctionExpression(node: ts.FunctionExpression): void { } // do not descend into fresh scopes + protected visitModuleDeclaration(): void { } // do not descend into fresh scopes + protected visitClassDeclaration(): void { } // do not descend into fresh scopes + protected visitArrowFunction(): void { } // do not descend into fresh scopes + protected visitFunctionExpression(): void { } // do not descend into fresh scopes /* tslint:enable:no-empty */ // need to make this public so it can be invoked outside of class diff --git a/src/utils/ScopedSymbolTrackingWalker.ts b/src/utils/ScopedSymbolTrackingWalker.ts index 8029eb3a8..522c7cdc3 100644 --- a/src/utils/ScopedSymbolTrackingWalker.ts +++ b/src/utils/ScopedSymbolTrackingWalker.ts @@ -10,14 +10,15 @@ import {Scope} from './Scope'; * from the SourceFile then -> Module -> Class -> Function */ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { - private languageServices: ts.LanguageService; - private typeChecker : ts.TypeChecker; + private typeChecker?: ts.TypeChecker; private scope: Scope; - constructor(sourceFile : ts.SourceFile, options : Lint.IOptions, languageServices : ts.LanguageService) { + constructor(sourceFile : ts.SourceFile, options : Lint.IOptions, program? : ts.Program) { super(sourceFile, options); - this.languageServices = languageServices; - this.typeChecker = this.languageServices.getProgram().getTypeChecker(); + + if (program) { + this.typeChecker = program.getTypeChecker(); + } } protected isExpressionEvaluatingToFunction(expression : ts.Expression) : boolean { @@ -39,12 +40,10 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { return true; } - if (expression.kind === ts.SyntaxKind.Identifier) { - const typeInfo : ts.DefinitionInfo[] = this.languageServices.getTypeDefinitionAtPosition('file.ts', expression.getStart()); - if (typeInfo != null && typeInfo[0] != null) { - if (typeInfo[0].kind === 'function' || typeInfo[0].kind === 'local function') { - return true; // variables with type function are OK to pass - } + if (expression.kind === ts.SyntaxKind.Identifier && this.typeChecker) { + const tsSymbol = this.typeChecker.getSymbolAtLocation(expression); + if (tsSymbol && tsSymbol.flags === ts.SymbolFlags.Function) { + return true; // variables with type function are OK to pass } return false; } @@ -57,6 +56,10 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { try { // seems like another tslint error of some sort + if (!this.typeChecker) { + return true; + } + const signature : ts.Signature = this.typeChecker.getResolvedSignature(expression); const expressionType : ts.Type = this.typeChecker.getReturnTypeOfSignature(signature); return this.isFunctionType(expressionType, this.typeChecker); @@ -66,6 +69,10 @@ export class ScopedSymbolTrackingWalker extends ErrorTolerantWalker { } } + if (!this.typeChecker) { + return true; + } + return this.isFunctionType(this.typeChecker.getTypeAtLocation(expression), this.typeChecker); } diff --git a/src/validTypeofRule.ts b/src/validTypeofRule.ts index cbb8775d8..7c3a56c4d 100644 --- a/src/validTypeofRule.ts +++ b/src/validTypeofRule.ts @@ -47,7 +47,7 @@ class ValidTypeofRuleWalker extends ErrorTolerantWalker { if (Rule.VALID_TERMS.indexOf(node.text) === -1) { const start: number = node.getStart(); const width: number = node.getWidth(); - this.addFailure(this.createFailure(start, width, Rule.FAILURE_STRING + this.getClosestTerm(node.text) + '?')); + this.addFailureAt(start, width, Rule.FAILURE_STRING + this.getClosestTerm(node.text) + '?'); } } @@ -118,6 +118,6 @@ class ValidTypeofRuleWalker extends ErrorTolerantWalker { } return matrix[b.length][a.length]; - }; + } /* tslint:enable:no-increment-decrement */ } diff --git a/test-data/NoCookies/NoCookiesPassingTestInput.ts b/test-data/NoCookies/NoCookiesPassingTestInput.ts new file mode 100644 index 000000000..62f0c5487 --- /dev/null +++ b/test-data/NoCookies/NoCookiesPassingTestInput.ts @@ -0,0 +1,14 @@ + +interface DocumentLikeAPI { + cookie: string; +} + +function documentLikeAPIFunction() : DocumentLikeAPI { + return null; +} + +// These usages are OK because they are not on the DOM document +var document2 : DocumentLikeAPI = documentLikeAPIFunction(); +document2.cookie = '...'; +document2.cookie = '...'; +documentLikeAPIFunction().cookie = '...'; \ No newline at end of file diff --git a/test-data/NoStringBasedSetImmediateTestInput.ts b/test-data/NoStringBasedSetImmediateTestInput.ts new file mode 100644 index 000000000..7ce3cbed9 --- /dev/null +++ b/test-data/NoStringBasedSetImmediateTestInput.ts @@ -0,0 +1,57 @@ + +var typedStringVariable = 'string variable'; +var functionVariable = () => {}; +var anyVariable : any = () => {}; +var createFunction : () => (() => void) = () => { return () => {} }; // function that produces a function +var untypedCreateFunction: () => any = () => {}; // function that produces a function +var stringFunction : () => string = () => { return ''; }; // function that produces a string + +// lambdas are OK +setImmediate(() => {}); +this.setImmediate(() => {}); +window.setImmediate(() => {}); +// functions are OK +setImmediate(function () {}); +this.setImmediate(function () {}); +window.setImmediate(function () {}); +// expressions of type function are OK +setImmediate(functionVariable); +this.setImmediate(functionVariable); +window.setImmediate(functionVariable); +var a = setImmediate(functionVariable); +var b = this.setImmediate(functionVariable); +var c = window.setImmediate(functionVariable); +setImmediate(createFunction()); +this.setImmediate(createFunction()); +window.setImmediate(createFunction()); + + + +// this is no longer a false positive +function invokeImmediate(functionArg : () => void) { + setImmediate(functionArg); +} + + +// these should all create violations +setImmediate("var x = 'should fail'"); // example 1 +setImmediate(typedStringVariable); // example 2 +setImmediate(anyVariable); // example 3 +setImmediate(untypedCreateFunction()); // example 4 +setImmediate(stringFunction()); // example 5 +this.setImmediate("var x = 'should fail'"); // example 6 +this.setImmediate(typedStringVariable); // example 7 +this.setImmediate(anyVariable); // example 8 +this.setImmediate(untypedCreateFunction()); // example 9 +this.setImmediate(stringFunction()); // example 10 +window.setImmediate("var x = 'should fail'"); // example 11 +window.setImmediate(typedStringVariable); // example 12 +window.setImmediate(anyVariable); // example 13 +window.setImmediate(untypedCreateFunction()); // example 14 +window.setImmediate(stringFunction()); // example 15 +function invoke2Immediate(stringArg : string) { + setImmediate(stringArg); // example 16 +} +function invoke3Immediate(anyArg : any) { + setImmediate(anyArg); // example 17 +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetIntervalTestInput.ts b/test-data/NoStringBasedSetIntervalTestInput.ts new file mode 100644 index 000000000..e8e24d872 --- /dev/null +++ b/test-data/NoStringBasedSetIntervalTestInput.ts @@ -0,0 +1,57 @@ + +var typedStringVariable = 'string variable'; +var functionVariable = () => {}; +var anyVariable : any = () => {}; +var createFunction : () => (() => void) = () => { return () => {} }; // function that produces a function +var untypedCreateFunction: () => any = () => {}; // function that produces a function +var stringFunction : () => string = () => { return ''; }; // function that produces a string + +// lambdas are OK +setInterval(() => {}); +this.setInterval(() => {}); +window.setInterval(() => {}); +// functions are OK +setInterval(function () {}); +this.setInterval(function () {}); +window.setInterval(function () {}); +// expressions of type function are OK +setInterval(functionVariable); +this.setInterval(functionVariable); +window.setInterval(functionVariable); +var a = setInterval(functionVariable); +var b = this.setInterval(functionVariable); +var c = window.setInterval(functionVariable); +setInterval(createFunction()); +this.setInterval(createFunction()); +window.setInterval(createFunction()); + + + +// this used to be a false positive. +function invokeInterval(functionArg : () => void) { + setInterval(functionArg); +} + + +// these should all create violations +setInterval("var x = 'should fail'"); // example 1 +setInterval(typedStringVariable); // example 2 +setInterval(anyVariable); // example 3 +setInterval(untypedCreateFunction()); // example 4 +setInterval(stringFunction()); // example 5 +this.setInterval("var x = 'should fail'"); // example 6 +this.setInterval(typedStringVariable); // example 7 +this.setInterval(anyVariable); // example 8 +this.setInterval(untypedCreateFunction()); // example 9 +this.setInterval(stringFunction()); // example 10 +window.setInterval("var x = 'should fail'"); // example 11 +window.setInterval(typedStringVariable); // example 12 +window.setInterval(anyVariable); // example 13 +window.setInterval(untypedCreateFunction()); // example 14 +window.setInterval(stringFunction()); // example 15 +function invokInterval2(stringArg : string) { + setInterval(stringArg); // example 16 +} +function invokeInterval3(anyArg : any) { + setInterval(anyArg); // example 17 +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-constructor.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-constructor.ts new file mode 100644 index 000000000..a99040777 --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-constructor.ts @@ -0,0 +1,6 @@ + +class MyClass { + constructor(arg1) { + setTimeout(arg1, 5); + } +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-parameters.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-parameters.ts new file mode 100644 index 000000000..02e2334ca --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-parameters.ts @@ -0,0 +1,8 @@ + +// these should all create violations +function invoke2(stringArg : string) { + setTimeout(stringArg); // example 16 +} +function invoke3(anyArg : any) { + setTimeout(anyArg); // example 17 +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-template-strings.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-template-strings.ts new file mode 100644 index 000000000..447123d74 --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutFailingTestInput-template-strings.ts @@ -0,0 +1,2 @@ +var data = 'alert(1)'; +window.setTimeout(`${data}`, 200); \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case2.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case2.ts new file mode 100644 index 000000000..4dcafde90 --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case2.ts @@ -0,0 +1,67 @@ +var globalProp1: () => void; +var globalProp2 = () => {}; +var globalProp3 = function () {}; + +function globalFunction1() {} + +setTimeout(globalProp1, 5); +setTimeout(globalProp2, 5); +setTimeout(globalProp3, 5); +setTimeout(globalFunction1, 5); + +module SetTimeoutModule { + + var moduleProp1: () => void; + var moduleProp2 = () => {}; + var moduleProp3 = function () {}; + + function moduleFunction1() { + setTimeout(moduleProp1, 5); + setTimeout(moduleProp2, 5); + setTimeout(moduleProp3, 5); + setTimeout(moduleFunction1, 5); + } + + class View { + + private property1: () => void; + private property2 = () => {}; + private property3 = function () {}; + + private static property4: () => void; + private static property5 = () => {}; + private static property6 = function () {}; + + private static method1() {} + + private method2(): void { + setTimeout(View.method1, 5); + setTimeout(View.property4, 5); + setTimeout(View.property5, 5); + setTimeout(View.property6, 5); + + setTimeout(this.method2, 5); + setTimeout(this.property1, 5); + setTimeout(this.property2, 5); + + setTimeout(globalProp1, 5); + setTimeout(globalProp2, 5); + setTimeout(globalProp3, 5); + setTimeout(globalFunction1, 5); + + setTimeout(moduleProp1, 5); + setTimeout(moduleProp2, 5); + setTimeout(moduleProp3, 5); + setTimeout(moduleFunction1, 5); + + setTimeout(this.property1, 5); + + var f = function() { + setTimeout(this.method2, 5); + setTimeout(this.property1, 5); + setTimeout(this.property2, 5); + setTimeout(this.property3, 5); + }; + } + } +} diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case3.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case3.ts new file mode 100644 index 000000000..2988c2c4a --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-case3.ts @@ -0,0 +1,22 @@ + +var functionVariable = () => {}; +var createFunction : () => (() => void) = () => { return () => {}}; // function that produces a function + +// lambdas are OK +setTimeout(() => {}); +this.setTimeout(() => {}); +window.setTimeout(() => {}); +// functions are OK +setTimeout(function () {}); +this.setTimeout(function () {}); +window.setTimeout(function () {}); +// expressions of type function are OK +setTimeout(functionVariable); +this.setTimeout(functionVariable); +window.setTimeout(functionVariable); +var a = setTimeout(functionVariable); +var b = this.setTimeout(functionVariable); +var c = window.setTimeout(functionVariable); +setTimeout(createFunction()); +this.setTimeout(createFunction()); +window.setTimeout(createFunction()); \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-formerFalsePositive.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-formerFalsePositive.ts new file mode 100644 index 000000000..f42df45db --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-formerFalsePositive.ts @@ -0,0 +1,5 @@ + +function invoke(functionArg1 : () => void, functionArg2 = () => {}) { + setTimeout(functionArg1); + setTimeout(functionArg2); +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-functionParamFunction.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-functionParamFunction.ts new file mode 100644 index 000000000..2e44d81cb --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-functionParamFunction.ts @@ -0,0 +1,3 @@ +function foo(callback: Function) { + setTimeout(callback, 0); +} diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-issue46.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-issue46.ts new file mode 100644 index 000000000..b2ed44b62 --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-issue46.ts @@ -0,0 +1,22 @@ +class TestClassIssue46 { + + constructor(private window) { + + //Arrow Functions + setTimeout(() => { }, 100); + window.setTimeout(() => { }, 100); + + //Standard Functions + setTimeout(function () { }, 100); + window.setTimeout(function () { }, 100); + + //Strings + const alertNum = 1; + setTimeout("alert(" + alertNum + ")", 100); + window.setTimeout("alert(" + alertNum + ")", 100); + + //TS Template Strings + setTimeout(`alert(${alertNum})`, 100); + window.setTimeout(`alert(${alertNum})`, 100); + } +} \ No newline at end of file diff --git a/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-shadowedVariables.ts b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-shadowedVariables.ts new file mode 100644 index 000000000..f12dc6c5b --- /dev/null +++ b/test-data/NoStringBasedSetTimeout/NoStringBasedSetTimeoutTestInput-shadowedVariables.ts @@ -0,0 +1,51 @@ + +var globalProp1: () => void; +var globalProp2 = () => {}; +var globalProp3 = function () {}; + +function globalFunctionShadow1() {} + +module SetTimeoutModule { + + var moduleProp1: () => void; + var moduleProp2 = () => {}; + var moduleProp3 = function () {}; + + var globalProp1: any; // ! shadow ! + var globalFunctionShadow1 = 'not a function'; // ! shadow ! + + function moduleFunction1(globalProp2: any, moduleProp1: any) { // ! shadow ! + setTimeout(moduleProp1, 5); // should fail + setTimeout(moduleProp2, 5); // should pass + setTimeout(globalFunction1, 5); // should fail + setTimeout(globalProp2, 5); // should fail + } + + class View { + + constructor(moduleProp1, globalProp3) { + setTimeout(globalProp1, 5); // should fail + setTimeout(moduleProp1, 5); // should fail + setTimeout(globalProp2, 5); // should pass + } + + private method2(globalProp3: any): void { // ! shadow ! + var f = function(moduleProp1) { // ! shadow ! + var x = (moduleProp2) => { + setTimeout(globalProp1, 5); // should fail + setTimeout(globalProp2, 5); // should pass + setTimeout(globalProp3, 5); // should fail + setTimeout(globalFunction1, 5); // should fail + + setTimeout(moduleProp1, 5); // should fail + setTimeout(moduleProp2, 5); // should fail + setTimeout(moduleProp3, 5); // should pass + } + }; + } + + public set someSetter(moduleProp1: string) { + setTimeout(moduleProp1, 5); // should fail + } + } +} diff --git a/tsconfig.json b/tsconfig.json index dea303271..562ce7219 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,28 @@ { - "compilerOptions": { - "module": "commonjs", - "moduleResolution": "node", - "target": "es5", - "outDir": "dist", - "noUnusedLocals": true, - "noUnusedParameters": true - }, - "exclude": [ - ".tscache", - "dist", - "node_modules", - "templates", - "test-data" - ] -} \ No newline at end of file + "compilerOptions": { + "alwaysStrict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitAny": false, + "noImplicitThis": false, + "strictNullChecks": false, + "declaration": true, + "jsx": "react", + "lib": [ + "es6", + "dom", + "scripthost" + ], + "module": "commonjs", + "outDir": "dist/src", + "removeComments": true, + "sourceMap": true, + "target": "es5" + }, + "include": [ + "src" + ] +} diff --git a/tsconfig.testdata.json b/tsconfig.testdata.json new file mode 100644 index 000000000..a8ea8d6f1 --- /dev/null +++ b/tsconfig.testdata.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "declaration": true, + "jsx": "react", + "module": "commonjs", + "outDir": "dist/test-data", + "removeComments": true, + "sourceMap": true, + "target": "es5" + }, + "include": [ "test-data" ] +} diff --git a/tslint-warnings.csv b/tslint-warnings.csv index aa42ff91b..772ab2bfb 100644 --- a/tslint-warnings.csv +++ b/tslint-warnings.csv @@ -33,6 +33,7 @@ CWE 710 - Coding Standards Violation" label-position,Only allows labels in sensible locations.,TSLINT1SMBSHV,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,670,"CWE 670 - Always-Incorrect Control Flow Implementation" linebreak-style,Enforces a consistent linebreak style.,TSLINT1QRFV7G,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" +match-default-export-name,Requires that a default import have the same name as the declaration it imports. Does nothing for anonymous default exports.,TSLINT10I4J1H,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" max-classes-per-file,A file may not contain more than the specified number of classes,TSLINT1DB5DER,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, max-file-line-count,Requires files to remain under a certain number of lines,TSLINT1RKDRG1,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" @@ -48,7 +49,8 @@ missing-jsdoc,All files must have a top level JSDoc comment.,TSLINT117J7CN,tslin CWE 710 - Coding Standards Violation" mocha-avoid-only,"Do not invoke Mocha's describe.only, it.only or context.only functions.",TSLINT1M1BHOM,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, new-parens,Requires parentheses when invoking a constructor via the `new` keyword.,TSLINTJF199B,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" -no-any,Diallows usages of `any` as a type declaration.,TSLINTKSGO5V,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality +newline-before-return,Enforces blank line before return when not the only line in the block.,TSLINT1QRE306,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +no-any,Disallows usages of `any` as a type declaration.,TSLINTKSGO5V,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-arg,Disallows use of `arguments.callee`.,TSLINTKSGO99,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" @@ -88,6 +90,7 @@ no-document-write,Do not use document.write,TSLINTMGIOVQ,tslint,SDL,Error,Critic CWE 85 - Doubled Character XSS Manipulations" no-duplicate-case,Do not use duplicate case labels in switch statements.,TSLINT3MSPFV,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" +no-duplicate-super,Warns if 'super()' appears twice in a constructor.,TSLINTCBE8BK,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-duplicate-variable,Disallows duplicate variable declarations in the same block scope.,TSLINT6TMGHL,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" no-empty,Disallows empty blocks.,TSLINTJ99V50,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" no-empty-interface,Forbids empty interfaces.,TSLINTEPMP7K,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality @@ -109,26 +112,29 @@ CWE 116 - Improper Encoding or Escaping of Output" no-function-expression,Do not use function expressions; use arrow functions (lambdas) instead.,TSLINT1SUK540,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-http-string,Do not use strings that start with 'http:'. URL strings should start with 'https:'. ,TSLINT1IH80PN,tslint,SDL,Error,Critical,Mandatory,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,319,"CWE 319 - Cleartext Transmission of Sensitive Information" +no-import-side-effect,Avoid import statements with side-effect.,TSLINTDMTAKK,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-increment-decrement,Avoid use of increment and decrement operators particularly as part of complicated expressions,TSLINTEJN48,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-inner-html,"Do not write values to innerHTML, outerHTML, or set HTML using the JQuery html() function.",TSLINT1SKOIBH,tslint,SDL,Error,Critical,Mandatory,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"79, 85, 710","CWE 79 - Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') CWE 85 - Doubled Character XSS Manipulations CWE 710 - Coding Standards Violation" no-invalid-regexp,Do not use invalid regular expression strings in the RegExp constructor.,TSLINT18FB6OK,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +no-invalid-template-strings,Warns on use of `${` in non-template strings.,TSLINT10TRETE,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-invalid-this,Disallows using the `this` keyword outside of classes.,TSLINTD2VI5V,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-jquery-raw-elements,Do not create HTML elements using JQuery and string concatenation. It is error prone and can hide subtle defects.,TSLINTBQ3MR2,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-misused-new,Warns on apparent attempts to define constructors for interfaces or `new` for classes.,TSLINTL96MA6,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-multiline-string,Do not declare multiline strings,TSLINT10K5P9U,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +no-non-null-assertion,Disallows non-null assertions.,TSLINTNO75FN,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-octal-literal,Do not use octal literals or escaped octal sequences,TSLINT1F5BIM0,tslint,SDL,Error,Critical,Mandatory,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-parameter-properties,Disallows parameter properties in class constructors.,TSLINT1FFCD4S,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +no-reference-import,Don't if you import "foo" anyway.,TSLINT178RR3K,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-regex-spaces,Do not use multiple spaces in a regular expression literal. Similar to the ESLint no-regex-spaces rule,TSLINT1T5TJ80,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-reserved-keywords,"Do not use reserved keywords as names of local variables, fields, functions, or other identifiers.",TSLINT14J77I2,tslint,SDL,Error,Critical,Mandatory,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,398,"CWE 398 - Indicator of Poor Code Quality" no-shadowed-variable,Disallows shadowing variable declarations.,TSLINTH3IPT3,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-single-line-block-comment,Avoid single line block comments; use single line comments instead,TSLINT1GP42PU,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" -no-sparse-arrays,"Do not use sparse arrays. Sparse arrays contain empty slots, most frequently due to multiple commas being used in an array literal.",TSLINT1FQ43R6,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality -CWE 710 - Coding Standards Violation" +no-sparse-arrays,Forbids array literals to contain missing elements.,TSLINT1FQ43R6,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-stateless-class,A stateless class represents a failure in the object oriented design of the system.,TSLINT1HFBCGO,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-string-based-set-immediate,Do not use the version of setImmediate that accepts code as a string argument.,TSLINT1HQR2RT,tslint,SDL,Error,Critical,Mandatory,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"95, 676, 242, 116","CWE 95 - Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection') @@ -155,6 +161,7 @@ CWE 710 - Coding Standards Violation" no-typeof-undefined,Do not use the idiom typeof `x === 'undefined'`. You can safely use the simpler x === undefined or perhaps x == null if you want to check for either null or undefined.,TSLINTQLSFMV,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-unnecessary-bind,Do not bind `this` as the context for a function literal or lambda expression.,TSLINT1TO0VN1,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" +no-unnecessary-callback-wrapper,"Replaces `x => f(x)` with just `f`. To catch more cases, enable `only-arrow-functions` and `arrow-return-shorthand` too.",TSLINT1PRG22H,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" no-unnecessary-field-initialization,Do not unnecessarily initialize the fields of a class to values they already have.,TSLINT1IR8ES9,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" no-unnecessary-initializer,Forbids a 'var'/'let' statement or destructuring initializer to be initialized to 'undefined'.,TSLINT1TVQD22,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, @@ -165,8 +172,9 @@ CWE 710 - Coding Standards Violation" no-unnecessary-qualifier,Warns when a namespace qualifier (`A.x`) is unnecessary.,TSLINTKOT746,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-unnecessary-semicolons,Remove unnecessary semicolons,TSLINTEL0RJQ,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" -no-unsafe-any,Warns when using an expression of type 'any' in an unsafe way. Type casts and tests are allowed. -Expressions that work on all values (such as '"" + x') are allowed.,TSLINTE73MOI,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +no-unsafe-any,Warns when using an expression of type 'any' in a dynamic way. Uses are only allowed if they would work for `{} | null | undefined`. +Type casts and tests are allowed. +Expressions that work on all values (such as `"" + x`) are allowed.,TSLINTE73MOI,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, no-unsafe-finally,"Disallows control flow statements, such as `return`, `continue`, `break` and `throws` in finally blocks.",TSLINT1QMOM2N,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 584, 710","CWE 398 - Indicator of Poor Code Quality CWE 584 - Return Inside Finally Block CWE 710 - Coding Standards Violation" @@ -174,9 +182,6 @@ no-unsupported-browser-code,Avoid writing browser-specific code for unsupported no-unused-expression,Disallows unused expression statements.,TSLINT4EPRRU,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 563, 710","CWE 398 - Indicator of Poor Code Quality CWE 563 - Assignment to Variable without Use ('Unused Variable') CWE 710 - Coding Standards Violation" -no-unused-new,Disallows unused 'new' expression statements.,TSLINT1ELSO8A,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 563, 710","CWE 398 - Indicator of Poor Code Quality -CWE 563 - Assignment to Variable without Use ('Unused Variable') -CWE 710 - Coding Standards Violation" no-use-before-declare,Disallows usage of variables before their declaration.,TSLINTUU9UNF,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 456, 710","CWE 398 - Indicator of Poor Code Quality CWE 456 - Missing Initialization of a Variable CWE 710 - Coding Standards Violation" @@ -204,11 +209,12 @@ possible-timing-attack,Avoid timing attacks by not making direct comaprisons to CWE 749 - Exposed Dangerous Method or Function" prefer-array-literal,Use array literal syntax when declaring or instantiating array types.,TSLINTCHFS93,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" -prefer-const,Requires that variable declarations use `const` instead of `let` if possible.,TSLINTGOC17R,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 705, 710","CWE 398 - Indicator of Poor Code Quality +prefer-const,Requires that variable declarations use `const` instead of `let` and `var` if possible.,TSLINTGOC17R,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 705, 710","CWE 398 - Indicator of Poor Code Quality CWE 705 - Incorrect Control Flow Scoping CWE 710 - Coding Standards Violation" prefer-for-of,Recommends a 'for-of' loop over a standard 'for' loop if the index is only used to access the array being iterated.,TSLINT51MHG7,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, prefer-method-signature,Prefer `foo(): void` over `foo: () => void` in interfaces and types.,TSLINT1LVIQFA,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, +prefer-template,Prefer a template expression over string literal concatenation.,TSLINT1BUICJ8,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" promise-function-async,Requires any function or method that returns a promise to be marked async.,TSLINT1L1TRF8,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, promise-must-complete,"When a Promise instance is created, then either the reject() or resolve() parameter must be called on it within all code branches in the scope.",TSLINT4SIARK,tslint,Non-SDL,Error,Critical,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, quotemark,Requires single or double quotes for string literals.,TSLINTU8MMGA,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality @@ -242,17 +248,19 @@ CWE 351 - Insufficient Type Distinction CWE 480 - Use of Incorrect Operator CWE 704 - Incorrect Type Conversion or Cast CWE 710 - Coding Standards Violation" +return-undefined,Prefer `return;` in void functions and `return undefined;` in value-returning functions.,TSLINTFU39OI,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" semicolon,Enforces consistent semicolon usage at the end of every statement.,TSLINT1L591RI,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" strict-boolean-expressions,"Restricts the types allowed in boolean expressions. By default only booleans are allowed. The following nodes are checked: -* Arguments to the '!', '&&', and '||' operators -* The condition in a conditional expression ('cond ? x : y') -* Conditions for 'if', 'for', 'while', and 'do-while' statements.",TSLINTRUBPNF,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, + +* Arguments to the `!`, `&&`, and `||` operators +* The condition in a conditional expression (`cond ? x : y`) +* Conditions for `if`, `for`, `while`, and `do-while` statements.",TSLINTRUBPNF,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,, switch-default,Require a `default` case in all `switch` statements.,TSLINTKNBMK7,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 474, 710","CWE 398 - Indicator of Poor Code Quality CWE 474 - Use of Function with Inconsistent Implementations CWE 710 - Coding Standards Violation" -trailing-comma,"Requires or disallows trailing commas in array and object literals, destructuring assignments, function and tuple typings, named imports and function parameters.",TSLINT1R9EG1T,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" +trailing-comma,"Requires or disallows trailing commas in array and object literals, destructuring assignments, function typings, named imports and exports and function parameters.",TSLINT1R9EG1T,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,710,"CWE 710 - Coding Standards Violation" triple-equals,Requires `===` and `!==` in place of `==` and `!=`.,TSLINT1A3MGIF,tslint,Non-SDL,Warning,Moderate,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality CWE 710 - Coding Standards Violation" typedef,Requires type definitions to exist.,TSLINT1GMMOCC,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,"398, 710","CWE 398 - Indicator of Poor Code Quality diff --git a/tslint.json b/tslint.json index 8110537cb..009ba2ef3 100644 --- a/tslint.json +++ b/tslint.json @@ -3,25 +3,13 @@ "dist/src" ], "rules": { + "array-type": [true, "array"], "arrow-return-shorthand": false, "await-promise": false, - "no-boolean-literal-compare": false, - "no-floating-promises": false, - "no-misused-new": true, - "no-unbound-method": false, - "no-unnecessary-initializer": true, - "no-unnecessary-qualifier": false, - "no-unsafe-any": false, - "prefer-function-over-method": false, - "prefer-method-signature": true, - "strict-type-predicates": false, "adjacent-overload-signatures": false, "align": [ false ], - "array-type": [ - false - ], "arrow-parens": false, "ban": false, "callable-types": true, @@ -95,6 +83,7 @@ "no-backbone-get-set-outside-model": true, "no-banned-terms": true, "no-bitwise": true, + "no-boolean-literal-compare": false, "no-conditional-assignment": true, "no-consecutive-blank-lines": [ true @@ -120,6 +109,7 @@ "no-document-write": true, "no-duplicate-case": true, "no-duplicate-parameter-names": true, + "no-duplicate-super": true, "no-duplicate-variable": true, "no-empty": true, "no-empty-interface": true, @@ -127,6 +117,7 @@ "no-empty-line-after-opening-brace": false, "no-eval": true, "no-exec-script": true, + "no-floating-promises": false, "no-for-in": true, "no-for-in-array": false, "no-function-constructor-with-string-args": true, @@ -136,30 +127,32 @@ "http://www.example.com/?.*", "http://www.examples.com/?.*" ], + "no-import-side-effect": true, "no-increment-decrement": true, - "no-inferrable-types": [ - false - ], + "no-inferrable-types": false, "no-inferred-empty-object-type": false, "no-inner-html": true, "no-internal-module": false, "no-invalid-regexp": true, + "no-invalid-template-strings": true, "no-invalid-this": true, "no-jquery-raw-elements": true, "no-magic-numbers": false, "no-mergeable-namespace": false, "no-missing-visibility-modifiers": true, - "no-multiline-string": true, + "no-misused-new": true, + "no-multiline-string": [ true ], "no-multiple-var-decl": true, "no-namespace": false, "no-null-keyword": false, "no-octal-literal": true, "no-parameter-properties": false, "no-reference": true, + "no-reference-import": true, "no-regex-spaces": true, "no-require-imports": true, "no-reserved-keywords": true, - "no-shadowed-variable": false, + "no-shadowed-variable": true, "no-single-line-block-comment": true, "no-sparse-arrays": true, "no-stateless-class": true, @@ -172,16 +165,19 @@ "no-switch-case-fall-through": true, "no-trailing-whitespace": true, "no-typeof-undefined": true, + "no-unbound-method": false, "no-unnecessary-bind": true, + "no-unnecessary-callback-wrapper": true, "no-unnecessary-field-initialization": true, + "no-unnecessary-initializer": true, "no-unnecessary-local-variable": true, "no-unnecessary-override": true, + "no-unnecessary-qualifier": false, "no-unnecessary-semicolons": true, + "no-unsafe-any": false, "no-unsafe-finally": true, "no-unsupported-browser-code": false, "no-unused-expression": true, - "no-unused-imports": false, - "no-unused-new": true, "no-use-before-declare": true, "no-var-keyword": true, "no-var-requires": true, @@ -190,7 +186,7 @@ "no-with-statement": true, "non-literal-require": true, "object-literal-key-quotes": [ - false, + true, "as-needed" ], "object-literal-shorthand": false, @@ -202,12 +198,8 @@ "check-else", "check-whitespace" ], - "one-variable-per-declaration": [ - true - ], - "only-arrow-functions": [ - false - ], + "one-variable-per-declaration": true, + "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"], "ordered-imports": [ false ], @@ -215,7 +207,11 @@ "prefer-array-literal": true, "prefer-const": true, "prefer-for-of": false, + "prefer-function-over-method": false, + "prefer-method-signature": true, + "prefer-template": false, "prefer-type-cast": true, + "strict-type-predicates": false, "promise-function-async": false, "promise-must-complete": true, "quotemark": [ diff --git a/typings/react.d.ts b/typings/react.d.ts index 2bdd12b35..9659ab16a 100644 --- a/typings/react.d.ts +++ b/typings/react.d.ts @@ -159,7 +159,7 @@ declare namespace __React { type ReactInstance = Component | Element; // Base component for plain JS classes - class Component implements ComponentLifecycle { + class Component { constructor(props?: P, context?: any); setState(f: (prevState: S, props: P) => S, callback?: () => any): void; setState(state: S, callback?: () => any): void;