diff --git a/README.md b/README.md
index 9000fbf11..aa6401715 100644
--- a/README.md
+++ b/README.md
@@ -8,13 +8,13 @@ tslint-microsoft-contrib
A set of [TSLint](https://github.com/palantir/tslint) rules used on some Microsoft projects.
-Version 5.0.3 (Stable)
+Version 5.1.0 (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 5.0.4 (In-Development)
+Version 5.1.1 (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`
@@ -26,7 +26,7 @@ Installation
Alternately, you can download the files directly from GitHub:
-* [5.0.3](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-5.0.3)
+* [5.1.0](https://github.com/Microsoft/tslint-microsoft-contrib/tree/npm-5.1.0)
#### TSLint and corresponding tslint-microsoft-contrib version
@@ -143,7 +143,7 @@ Rule Name | Description | Since
`react-a11y-img-has-alt` | Enforce that an `img` element contains the `alt` attribute or `role='presentation'` for a decorative image. All images must have `alt` text to convey their purpose and meaning to **screen reader users**. Besides, the `alt` attribute specifies an alternate text for an image, if the image cannot be displayed. This rule accepts as a parameter a string array for tag names other than img to also check. For example, if you use a custom tag named 'Image' then configure the rule with: `[true, ['Image']]`
References:
[Web Content Accessibility Guidelines 1.0](https://www.w3.org/TR/WCAG10/wai-pageauth.html#tech-text-equivalent)
[ARIA Presentation Role](https://www.w3.org/TR/wai-aria/roles#presentation)
[WCAG Rule 31: If an image has an alt or title attribute, it should not have a presentation role](http://oaa-accessibility.org/wcag20/rule/31/) | 2.0.11
`react-a11y-lang` | For accessibility of your website, HTML elements must have a lang attribute and the attribute must be a valid language code.
References:
* [H58: Using language attributes to identify changes in the human language](https://www.w3.org/TR/WCAG20-TECHS/H58.html)
* [lang attribute must have a valid value](https://dequeuniversity.com/rules/axe/1.1/valid-lang)
[List of ISO 639-1 codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) | 2.0.11
`react-a11y-meta` | For accessibility of your website, HTML meta elements must not have http-equiv="refresh". | 2.0.11
-`react-a11y-props` | For accessibility of your website, enforce all `aria-*` attributes are valid. Elements cannot use an invalid `aria-*` attribute. This rule will fail if it finds an `aria-*` attribute that is not listed in [WAI-ARIA states and properties](https://www.w3.org/TR/wai-aria/states_and_properties#state_prop_def). | 2.0.11
+`react-a11y-props` | For accessibility of your website, enforce all `aria-*` attributes are valid. Elements cannot use an invalid `aria-*` attribute. This rule will fail if it finds an `aria-*` attribute that is not listed in [WAI-ARIA states and properties](https://www.w3.org/WAI/PF/aria/states_and_properties#state_prop_values). | 2.0.11
`react-a11y-proptypes` | For accessibility of your website, enforce the type of aria state and property values are correct. | 2.0.11
`react-a11y-role-has-required-aria-props` | For accessibility of your website, elements with aria roles must have all required attributes according to the role.
References:
[ARIA Definition of Roles](https://www.w3.org/TR/wai-aria/roles#role_definitions)
[WCAG Rule 90: Required properties and states should be defined](http://oaa-accessibility.org/wcag20/rule/90/)
[WCAG Rule 91: Required properties and states must not be empty](http://oaa-accessibility.org/wcag20/rule/91/)
| 2.0.11
`react-a11y-role-supports-aria-props` | For accessibility of your website, enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. Many aria attributes (states and properties) can only be used on elements with particular roles. Some elements have implicit roles, such as ``, which will be resolved to `role='link'`. A reference for the implicit roles can be found at [Default Implicit ARIA Semantics](https://www.w3.org/TR/html-aria/#sec-strong-native-semantics).
References:
* [ARIA attributes can only be used with certain roles](http://oaa-accessibility.org/wcag20/rule/87/)
* [Check aria properties and states for valid roles and properties](http://oaa-accessibility.org/wcag20/rule/84/)
* [Check that 'ARIA-' attributes are valid properties and states](http://oaa-accessibility.org/wcag20/rule/93/)| 2.0.11
diff --git a/exportNameRule.js b/exportNameRule.js
index bfa0ae55c..98b124b40 100644
--- a/exportNameRule.js
+++ b/exportNameRule.js
@@ -50,6 +50,24 @@ var Rule = (function (_super) {
return Rule;
}(Lint.Rules.AbstractRule));
exports.Rule = Rule;
+function isExportedDeclaration(element) {
+ return AstUtils_1.AstUtils.hasModifier(element.modifiers, ts.SyntaxKind.ExportKeyword);
+}
+function isExportStatement(node) {
+ return ts.isExportAssignment(node) || ts.isExportDeclaration(node);
+}
+function getExportsFromStatement(node) {
+ if (ts.isExportAssignment(node)) {
+ return [[node.expression.getText(), node.expression]];
+ }
+ else {
+ var symbolAndNodes_1 = [];
+ node.exportClause.elements.forEach(function (e) {
+ symbolAndNodes_1.push([e.name.getText(), node]);
+ });
+ return symbolAndNodes_1;
+ }
+}
var ExportNameWalker = (function (_super) {
__extends(ExportNameWalker, _super);
function ExportNameWalker() {
@@ -57,19 +75,15 @@ var ExportNameWalker = (function (_super) {
}
ExportNameWalker.prototype.visitSourceFile = function (node) {
var _this = this;
- var singleExport = node.statements.filter(function (element) {
- return element.kind === ts.SyntaxKind.ExportAssignment;
- });
+ var singleExport = node.statements.filter(isExportStatement);
if (singleExport.length === 1) {
- var exportAssignment = singleExport[0];
- this.validateExport(exportAssignment.expression.getText(), exportAssignment.expression);
+ var symbolsAndNodes = getExportsFromStatement(singleExport[0]);
+ if (symbolsAndNodes.length === 1) {
+ this.validateExport(symbolsAndNodes[0][0], symbolsAndNodes[0][1]);
+ }
return;
}
- var exportedTopLevelElements = [];
- node.statements.forEach(function (element) {
- var exportStatements = _this.getExportStatements(element);
- exportedTopLevelElements = exportedTopLevelElements.concat(exportStatements);
- });
+ var exportedTopLevelElements = node.statements.filter(isExportedDeclaration);
if (exportedTopLevelElements.length === 0) {
node.statements.forEach(function (element) {
if (element.kind === ts.SyntaxKind.ModuleDeclaration) {
@@ -81,31 +95,15 @@ var ExportNameWalker = (function (_super) {
this.validateExportedElements(exportedTopLevelElements);
};
ExportNameWalker.prototype.getExportStatementsWithinModules = function (moduleDeclaration) {
- var _this = this;
if (moduleDeclaration.body.kind === ts.SyntaxKind.ModuleDeclaration) {
return this.getExportStatementsWithinModules(moduleDeclaration.body);
}
else if (moduleDeclaration.body.kind === ts.SyntaxKind.ModuleBlock) {
- var exportStatements_1 = [];
var moduleBlock = moduleDeclaration.body;
- moduleBlock.statements.forEach(function (element) {
- exportStatements_1 = exportStatements_1.concat(_this.getExportStatements(element));
- });
- return exportStatements_1;
+ return moduleBlock.statements.filter(isExportedDeclaration);
}
return null;
};
- ExportNameWalker.prototype.getExportStatements = function (element) {
- var exportStatements = [];
- if (element.kind === ts.SyntaxKind.ExportAssignment) {
- var exportAssignment = element;
- this.validateExport(exportAssignment.expression.getText(), exportAssignment.expression);
- }
- else if (AstUtils_1.AstUtils.hasModifier(element.modifiers, ts.SyntaxKind.ExportKeyword)) {
- exportStatements.push(element);
- }
- return exportStatements;
- };
ExportNameWalker.prototype.validateExportedElements = function (exportedElements) {
if (exportedElements.length === 1) {
if (exportedElements[0].kind === ts.SyntaxKind.ModuleDeclaration ||
diff --git a/mochaNoSideEffectCodeRule.js b/mochaNoSideEffectCodeRule.js
index 736368513..772e5176e 100644
--- a/mochaNoSideEffectCodeRule.js
+++ b/mochaNoSideEffectCodeRule.js
@@ -145,6 +145,9 @@ var MochaNoSideEffectCodeRuleWalker = (function (_super) {
});
return;
}
+ if (/^this\.(retries|slow|timeout)\(.+\)$/.test(initializer.getText())) {
+ return;
+ }
if (initializer.getText() === 'moment()') {
return;
}
diff --git a/noFunctionExpressionRule.js b/noFunctionExpressionRule.js
index 6d09d9588..6ec04cf4b 100644
--- a/noFunctionExpressionRule.js
+++ b/noFunctionExpressionRule.js
@@ -48,7 +48,7 @@ var NoFunctionExpressionRuleWalker = (function (_super) {
node.getChildren().forEach(function (child) {
walker.walk(child);
});
- if (!walker.isAccessingThis) {
+ if (!walker.isAccessingThis && !node.asteriskToken) {
this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING);
}
_super.prototype.visitFunctionExpression.call(this, node);
diff --git a/noHttpStringRule.js b/noHttpStringRule.js
index e35ebce66..c4ad14db8 100644
--- a/noHttpStringRule.js
+++ b/noHttpStringRule.js
@@ -34,7 +34,7 @@ var Rule = (function (_super) {
severity: 'Critical',
level: 'Mandatory',
group: 'Security',
- recommendation: '[true, "http://www.example.com/?.*", "http://www.examples.com/?.*"],',
+ recommendation: '[true, "http://www.example.com/?.*", "http://localhost:?.*"],',
commonWeaknessEnumeration: '319'
};
Rule.FAILURE_STRING = 'Forbidden http url in string: ';
diff --git a/noInnerHtmlRule.js b/noInnerHtmlRule.js
index 65f572dca..37fe984e2 100644
--- a/noInnerHtmlRule.js
+++ b/noInnerHtmlRule.js
@@ -16,7 +16,7 @@ var ErrorTolerantWalker_1 = require("./utils/ErrorTolerantWalker");
var AstUtils_1 = require("./utils/AstUtils");
var FAILURE_INNER = 'Writing a string to the innerHTML property is insecure: ';
var FAILURE_OUTER = 'Writing a string to the outerHTML property is insecure: ';
-var FAILURE_JQUERY = 'Using the html() function to write a string to innerHTML is insecure: ';
+var FAILURE_HTML_LIB = 'Using the html() function to write a string to innerHTML is insecure: ';
var Rule = (function (_super) {
__extends(Rule, _super);
function Rule() {
@@ -44,8 +44,14 @@ var Rule = (function (_super) {
exports.Rule = Rule;
var NoInnerHtmlRuleWalker = (function (_super) {
__extends(NoInnerHtmlRuleWalker, _super);
- function NoInnerHtmlRuleWalker() {
- return _super !== null && _super.apply(this, arguments) || this;
+ function NoInnerHtmlRuleWalker(sourceFile, options) {
+ var _this = _super.call(this, sourceFile, options) || this;
+ _this.htmlLibExpressionRegex = /^(jquery|[$])/i;
+ var opt = _this.getOptions();
+ if (typeof opt[1] === 'object' && opt[1]['html-lib-matcher']) {
+ _this.htmlLibExpressionRegex = new RegExp(opt[1]['html-lib-matcher']);
+ }
+ return _this;
}
NoInnerHtmlRuleWalker.prototype.visitBinaryExpression = function (node) {
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
@@ -66,7 +72,10 @@ var NoInnerHtmlRuleWalker = (function (_super) {
var functionName = AstUtils_1.AstUtils.getFunctionName(node);
if (functionName === 'html') {
if (node.arguments.length > 0) {
- this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_JQUERY + node.getText());
+ var functionTarget = AstUtils_1.AstUtils.getFunctionTarget(node);
+ if (this.htmlLibExpressionRegex.test(functionTarget)) {
+ this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_HTML_LIB + node.getText());
+ }
}
}
_super.prototype.visitCallExpression.call(this, node);
diff --git a/noOctalLiteralRule.js b/noOctalLiteralRule.js
index c10cb3cdc..159558d82 100644
--- a/noOctalLiteralRule.js
+++ b/noOctalLiteralRule.js
@@ -45,18 +45,22 @@ var NoOctalLiteral = (function (_super) {
return _super !== null && _super.apply(this, arguments) || this;
}
NoOctalLiteral.prototype.visitNode = function (node) {
- if (node.kind === ts.SyntaxKind.StringLiteral) {
+ if (node.kind === ts.SyntaxKind.StringLiteral || node.kind === ts.SyntaxKind.FirstTemplateToken) {
this.failOnOctalString(node);
}
_super.prototype.visitNode.call(this, node);
};
NoOctalLiteral.prototype.failOnOctalString = function (node) {
- var match = /("|')(.*(\\-?[0-7]{1,3}(?![0-9])).*("|'))/g.exec(node.getText());
+ var match = /("|'|`)[^\\]*(\\+-?[0-7]{1,3}(?![0-9]))(?:.|\n|\t|\u2028|\u2029)*(?:\1)/g.exec(node.getText());
if (match) {
- var octalValue = match[3];
- var startOfMatch = node.getStart() + node.getText().indexOf(octalValue);
- var width = octalValue.length;
- this.addFailureAt(startOfMatch, width, Rule.FAILURE_STRING + octalValue);
+ var octalValue = match[2];
+ var backslashCount = octalValue.lastIndexOf('\\') + 1;
+ if (backslashCount % 2 === 1) {
+ octalValue = octalValue.substr(backslashCount - 1);
+ var startOfMatch = node.getStart() + node.getText().indexOf(octalValue);
+ var width = octalValue.length;
+ this.addFailureAt(startOfMatch, width, Rule.FAILURE_STRING + octalValue);
+ }
}
};
return NoOctalLiteral;
diff --git a/noSingleLineBlockCommentRule.js b/noSingleLineBlockCommentRule.js
index 51eb05385..076c2045c 100644
--- a/noSingleLineBlockCommentRule.js
+++ b/noSingleLineBlockCommentRule.js
@@ -50,7 +50,8 @@ var NoSingleLineBlockCommentRuleWalker = (function (_super) {
var tokenText = fullText.substring(range.pos, range.end);
if (tokenSyntaxKind === ts.SyntaxKind.MultiLineCommentTrivia
&& _this.isSingleLineComment(tokenText)
- && !_this.isTsLintSuppression(tokenText)) {
+ && !_this.isTsLintSuppression(tokenText)
+ && !_this.isFollowedByMoreCodeOnSameLine(fullText, range)) {
_this.addFailureAt(range.pos, range.end - range.pos, FAILURE_STRING);
}
});
@@ -62,6 +63,10 @@ var NoSingleLineBlockCommentRuleWalker = (function (_super) {
NoSingleLineBlockCommentRuleWalker.prototype.isTsLintSuppression = function (commentText) {
return /\/*\s*tslint:(enable|disable):.*/.test(commentText);
};
+ NoSingleLineBlockCommentRuleWalker.prototype.isFollowedByMoreCodeOnSameLine = function (fullText, range) {
+ var restOfText = fullText.substring(range.end);
+ return /^\s*\r?\n/.test(restOfText) === false;
+ };
return NoSingleLineBlockCommentRuleWalker;
}(Lint.RuleWalker));
//# sourceMappingURL=noSingleLineBlockCommentRule.js.map
\ No newline at end of file
diff --git a/noSuspiciousCommentRule.js b/noSuspiciousCommentRule.js
index c750b8649..1d82f6d71 100644
--- a/noSuspiciousCommentRule.js
+++ b/noSuspiciousCommentRule.js
@@ -26,7 +26,7 @@ var Rule = (function (_super) {
Rule.metadata = {
ruleName: 'no-suspicious-comment',
type: 'maintainability',
- description: 'Do not use suspicious comments, such as BUG, HACK, FIXME, LATER, LATER2, TODO',
+ description: "Do not use suspicious comments, such as " + SUSPICIOUS_WORDS.join(', '),
options: null,
optionsDescription: '',
typescriptOnly: true,
diff --git a/noUnnecessaryLocalVariableRule.js b/noUnnecessaryLocalVariableRule.js
index aeee41477..885d180b3 100644
--- a/noUnnecessaryLocalVariableRule.js
+++ b/noUnnecessaryLocalVariableRule.js
@@ -10,8 +10,8 @@ var __extends = (this && this.__extends) || (function () {
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
-var ts = require("typescript");
var Lint = require("tslint");
+var tsutils = require("tsutils");
var ErrorTolerantWalker_1 = require("./utils/ErrorTolerantWalker");
var FAILURE_STRING = 'Unnecessary local variable: ';
var Rule = (function (_super) {
@@ -42,7 +42,9 @@ exports.Rule = Rule;
var UnnecessaryLocalVariableRuleWalker = (function (_super) {
__extends(UnnecessaryLocalVariableRuleWalker, _super);
function UnnecessaryLocalVariableRuleWalker() {
- return _super !== null && _super.apply(this, arguments) || this;
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.variableUsages = tsutils.collectVariableUsage(_this.getSourceFile());
+ return _this;
}
UnnecessaryLocalVariableRuleWalker.prototype.visitBlock = function (node) {
this.validateStatementArray(node.statements);
@@ -61,7 +63,7 @@ var UnnecessaryLocalVariableRuleWalker = (function (_super) {
_super.prototype.visitDefaultClause.call(this, node);
};
UnnecessaryLocalVariableRuleWalker.prototype.visitModuleDeclaration = function (node) {
- if (node.body != null && node.body.kind === ts.SyntaxKind.ModuleBlock) {
+ if (node.body != null && tsutils.isModuleBlock(node.body)) {
this.validateStatementArray(node.body.statements);
}
_super.prototype.visitModuleDeclaration.call(this, node);
@@ -73,34 +75,41 @@ var UnnecessaryLocalVariableRuleWalker = (function (_super) {
var lastStatement = statements[statements.length - 1];
var nextToLastStatement = statements[statements.length - 2];
var returnedVariableName = this.tryToGetReturnedVariableName(lastStatement);
- var declaredVariableName = this.tryToGetDeclaredVariableName(nextToLastStatement);
- if (returnedVariableName != null && declaredVariableName != null) {
- if (returnedVariableName === declaredVariableName) {
- this.addFailureAt(nextToLastStatement.getStart(), nextToLastStatement.getWidth(), FAILURE_STRING + returnedVariableName);
- }
+ var declaredVariableIdentifier = this.tryToGetDeclaredVariable(nextToLastStatement);
+ if (declaredVariableIdentifier == null) {
+ return;
+ }
+ var declaredVariableName = declaredVariableIdentifier.text;
+ if (returnedVariableName != null
+ && declaredVariableName != null
+ && returnedVariableName === declaredVariableName
+ && this.variableIsOnlyUsedOnce(declaredVariableIdentifier)) {
+ this.addFailureAt(nextToLastStatement.getStart(), nextToLastStatement.getWidth(), FAILURE_STRING + returnedVariableName);
}
};
- UnnecessaryLocalVariableRuleWalker.prototype.tryToGetDeclaredVariableName = function (statement) {
- if (statement != null && statement.kind === ts.SyntaxKind.VariableStatement) {
- var variableStatement = statement;
- if (variableStatement.declarationList.declarations.length === 1) {
- var declaration = variableStatement.declarationList.declarations[0];
- if (declaration.name != null && declaration.name.kind === ts.SyntaxKind.Identifier) {
- return declaration.name.text;
+ UnnecessaryLocalVariableRuleWalker.prototype.tryToGetDeclaredVariable = function (statement) {
+ if (statement != null && tsutils.isVariableStatement(statement)) {
+ if (statement.declarationList.declarations.length === 1) {
+ var declaration = statement.declarationList.declarations[0];
+ if (declaration.name != null && tsutils.isIdentifier(declaration.name)) {
+ return declaration.name;
}
}
}
return null;
};
UnnecessaryLocalVariableRuleWalker.prototype.tryToGetReturnedVariableName = function (statement) {
- if (statement != null && statement.kind === ts.SyntaxKind.ReturnStatement) {
- var returnStatement = statement;
- if (returnStatement.expression != null && returnStatement.expression.kind === ts.SyntaxKind.Identifier) {
- return returnStatement.expression.text;
+ if (statement != null && tsutils.isReturnStatement(statement)) {
+ if (statement.expression != null && tsutils.isIdentifier(statement.expression)) {
+ return statement.expression.text;
}
}
return null;
};
+ UnnecessaryLocalVariableRuleWalker.prototype.variableIsOnlyUsedOnce = function (declaredVariableIdentifier) {
+ var usage = this.variableUsages.get(declaredVariableIdentifier);
+ return usage !== undefined && usage.uses.length === 1;
+ };
return UnnecessaryLocalVariableRuleWalker;
}(ErrorTolerantWalker_1.ErrorTolerantWalker));
//# sourceMappingURL=noUnnecessaryLocalVariableRule.js.map
\ No newline at end of file
diff --git a/package.json b/package.json
index 794d69378..6a872989d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tslint-microsoft-contrib",
- "version": "5.0.3",
+ "version": "5.1.0",
"description": "TSLint Rules for Microsoft",
"repository": {
"type": "git",
@@ -19,6 +19,10 @@
{
"name": "Hamlet D'Arcy",
"email": "hamlet.darcy@microsoft.com"
+ },
+ {
+ "name": "Josh Goldberg",
+ "email": "joshua.goldberg@microsoft.com"
}
],
"bugs": {
diff --git a/recommended_ruleset.js b/recommended_ruleset.js
index f8424a7a0..16fc118bb 100644
--- a/recommended_ruleset.js
+++ b/recommended_ruleset.js
@@ -19,7 +19,7 @@ module.exports = {
"no-eval": true,
"no-exec-script": true,
"no-function-constructor-with-string-args": true,
- "no-http-string": [true, "http://www.example.com/?.*", "http://www.examples.com/?.*"],
+ "no-http-string": [true, "http://www.example.com/?.*", "http://www.examples.com/?.*", "http://localhost:?.*"],
"no-inner-html": true,
"no-octal-literal": true,
"no-reserved-keywords": true,
diff --git a/tslint.json b/tslint.json
index a7cd44d38..a48d32066 100644
--- a/tslint.json
+++ b/tslint.json
@@ -13,7 +13,7 @@
"no-http-string": [
true,
"http://www.example.com/?.*",
- "http://www.examples.com/?.*"
+ "http://localhost:?.*"
],
"no-inner-html": true,
"no-octal-literal": true,
@@ -42,6 +42,7 @@
true,
"debug",
"info",
+ "error",
"log",
"time",
"timeEnd",
diff --git a/utils/MochaUtils.js b/utils/MochaUtils.js
index 3c3773c2b..da4aef004 100644
--- a/utils/MochaUtils.js
+++ b/utils/MochaUtils.js
@@ -32,9 +32,11 @@ var MochaUtils;
MochaUtils.isDescribe = isDescribe;
function isLifecycleMethod(call) {
var functionName = AstUtils_1.AstUtils.getFunctionName(call);
+ var callText = call.expression.getText();
return functionName === 'it' || functionName === 'specify'
|| functionName === 'before' || functionName === 'beforeEach' || functionName === 'beforeAll'
- || functionName === 'after' || functionName === 'afterEach' || functionName === 'afterAll';
+ || functionName === 'after' || functionName === 'afterEach' || functionName === 'afterAll'
+ || callText === 'it.skip' || callText === 'it.only';
}
MochaUtils.isLifecycleMethod = isLifecycleMethod;
})(MochaUtils = exports.MochaUtils || (exports.MochaUtils = {}));
diff --git a/utils/NoStringParameterToFunctionCallWalker.js b/utils/NoStringParameterToFunctionCallWalker.js
index d09ed5fbc..6838d6a1d 100644
--- a/utils/NoStringParameterToFunctionCallWalker.js
+++ b/utils/NoStringParameterToFunctionCallWalker.js
@@ -26,8 +26,22 @@ var NoStringParameterToFunctionCallWalker = (function (_super) {
};
NoStringParameterToFunctionCallWalker.prototype.validateExpression = function (node) {
var functionName = AstUtils_1.AstUtils.getFunctionName(node);
+ var functionTarget = AstUtils_1.AstUtils.getFunctionTarget(node);
+ var functionTargetType = this.getFunctionTargetType(node);
var firstArg = node.arguments[0];
if (functionName === this.targetFunctionName && firstArg != null) {
+ if (functionTarget) {
+ if (functionTargetType) {
+ if (!functionTargetType.match(/^(any|Window|Worker)$/)) {
+ return;
+ }
+ }
+ else {
+ if (!functionTarget.match(/^(this|window)$/)) {
+ return;
+ }
+ }
+ }
if (!this.isExpressionEvaluatingToFunction(firstArg)) {
var msg = this.failureString + firstArg.getFullText().trim().substring(0, 40);
this.addFailureAt(node.getStart(), node.getWidth(), msg);
diff --git a/utils/ScopedSymbolTrackingWalker.js b/utils/ScopedSymbolTrackingWalker.js
index 208701e3d..488ac0b32 100644
--- a/utils/ScopedSymbolTrackingWalker.js
+++ b/utils/ScopedSymbolTrackingWalker.js
@@ -23,6 +23,14 @@ var ScopedSymbolTrackingWalker = (function (_super) {
}
return _this;
}
+ ScopedSymbolTrackingWalker.prototype.getFunctionTargetType = function (expression) {
+ if (expression.expression.kind === ts.SyntaxKind.PropertyAccessExpression && this.typeChecker) {
+ var propExp = expression.expression;
+ var targetType = this.typeChecker.getTypeAtLocation(propExp.expression);
+ return this.typeChecker.typeToString(targetType);
+ }
+ return null;
+ };
ScopedSymbolTrackingWalker.prototype.isExpressionEvaluatingToFunction = function (expression) {
if (expression.kind === ts.SyntaxKind.ArrowFunction
|| expression.kind === ts.SyntaxKind.FunctionExpression) {