From 2e285e22efb7bdc031ded2721c52cf8e041b9901 Mon Sep 17 00:00:00 2001 From: mgechev Date: Sat, 18 Feb 2017 16:45:33 -0800 Subject: [PATCH] fix(rules): check for proper modifier in base class --- src/templatesUsePublicRule.ts | 15 ++++++----- test/templatesUsePublicRule.spec.ts | 40 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/templatesUsePublicRule.ts b/src/templatesUsePublicRule.ts index 8ec0719d7..b5710c607 100644 --- a/src/templatesUsePublicRule.ts +++ b/src/templatesUsePublicRule.ts @@ -1,7 +1,7 @@ import * as Lint from 'tslint'; import * as ts from 'typescript'; import {stringDistance} from './util/utils'; -import {getDeclaredProperties, getDeclaredMethods} from './util/classDeclarationUtils'; +import {getClassMembers} from './util/classDeclarationUtils'; import {Ng2Walker} from './angular/ng2Walker'; import {RecursiveAngularExpressionVisitor} from './angular/templates/recursiveAngularExpressionVisitor'; import * as e from '@angular/compiler/src/expression_parser/ast'; @@ -34,7 +34,8 @@ class SymbolAccessValidator extends RecursiveAngularExpressionVisitor { } ast = receiver; } - const allMembers = getDeclaredMethods(this.context.controller).concat(getDeclaredProperties(this.context.controller)); + const allMembers = getClassMembers(this.context.controller, this.languageService.getProgram().getTypeChecker()) + .map(s => s.declarations[0]); const member = allMembers.filter((m: any) => m.name && m.name.text === ast.name).pop(); if (member) { let isPublic = !member.modifiers || !member.modifiers @@ -73,14 +74,16 @@ class SymbolAccessValidator extends RecursiveAngularExpressionVisitor { } } -export class Rule extends Lint.Rules.AbstractRule { +export class Rule extends Lint.Rules.TypedRule { static FAILURE: string = 'The %s "%s" that you\'re trying to access does not exist in the class declaration.'; - public apply(sourceFile:ts.SourceFile): Lint.RuleFailure[] { + public applyWithProgram(sourceFile: ts.SourceFile, languageService: ts.LanguageService): Lint.RuleFailure[] { + const sf = languageService.getProgram().getSourceFiles().filter(sf => sf.fileName === sourceFile.fileName).pop(); return this.applyWithWalker( - new Ng2Walker(sourceFile, + new Ng2Walker(sf, this.getOptions(), { - expressionVisitorCtrl: SymbolAccessValidator + expressionVisitorCtrl: SymbolAccessValidator, + languageService })); } } diff --git a/test/templatesUsePublicRule.spec.ts b/test/templatesUsePublicRule.spec.ts index 86d339b0c..e53cc83f4 100644 --- a/test/templatesUsePublicRule.spec.ts +++ b/test/templatesUsePublicRule.spec.ts @@ -223,4 +223,44 @@ describe('templates-use-public', () => { assertSuccess('templates-use-public', source); }); }); + + describe('inheritance', () => { + it('should support inheritance chain', () => { + let source = ` + class Base { + readonly foo: any; + } + + @Component({ + selector: 'foobar', + template: '
{{ foo }}
+ }) + class Test extends Base {}`; + assertSuccess('templates-use-public', source); + }); + + it('should fail in case of protected inherited property', () => { + let source = ` + class Base { + readonly protected foo: any; + } + + @Component({ + selector: 'foobar', + template: '
{{ foo }}
+ }) + class Test extends Base {}`; + assertFailure('templates-use-public', source, { + message: 'You can bind only to public class members.', + startPosition: { + line: 7, + character: 29 + }, + endPosition: { + line: 7, + character: 32 + } + }); + }); + }); });