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) {