Skip to content

Commit

Permalink
fix(rules): consider members of base classes
Browse files Browse the repository at this point in the history
Fix #191
  • Loading branch information
mgechev committed Feb 19, 2017
1 parent 126677d commit d23d83c
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 124 deletions.
9 changes: 3 additions & 6 deletions src/angular/ng2Walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface Ng2WalkerConfig {
expressionVisitorCtrl?: RecursiveAngularExpressionVisitorCtr;
templateVisitorCtrl?: TemplateAstVisitorCtr;
cssVisitorCtrl?: CssAstVisitorCtrl;
languageService?: ts.LanguageService;
}

export class Ng2Walker extends Lint.RuleWalker {
Expand All @@ -41,11 +42,6 @@ export class Ng2Walker extends Lint.RuleWalker {
protected _metadataReader?: MetadataReader) {
super(sourceFile, _originalOptions);
this._metadataReader = this._metadataReader || ng2WalkerFactoryUtils.defaultMetadataReader();
this._config = Object.assign({
templateVisitorCtrl: BasicTemplateAstVisitor,
expressionVisitorCtrl: RecursiveAngularExpressionVisitor,
cssVisitorCtrl: BasicCssAstVisitor
}, this._config || {});

this._config = Object.assign({
templateVisitorCtrl: BasicTemplateAstVisitor,
Expand Down Expand Up @@ -169,7 +165,8 @@ export class Ng2Walker extends Lint.RuleWalker {
const referenceVisitor = new ReferenceCollectorVisitor();
const visitor =
new this._config.templateVisitorCtrl(
sourceFile, this._originalOptions, context, baseStart, this._config.expressionVisitorCtrl);
sourceFile, this._originalOptions, context, baseStart, this._config.expressionVisitorCtrl,
this._config.languageService);
compiler.templateVisitAll(referenceVisitor, roots, null);
visitor._variables = referenceVisitor.variables;
compiler.templateVisitAll(visitor, roots, context.controller);
Expand Down
10 changes: 6 additions & 4 deletions src/angular/templates/basicTemplateAstVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ const getExpressionDisplacement = (binding: any) => {


export interface RecursiveAngularExpressionVisitorCtr {
new(sourceFile: ts.SourceFile, options: Lint.IOptions, context: ComponentMetadata, basePosition: number);
new(sourceFile: ts.SourceFile, options: Lint.IOptions, context: ComponentMetadata, basePosition: number,
languageService?: ts.LanguageService);
}


export interface TemplateAstVisitorCtr {
new(sourceFile: ts.SourceFile, options: Lint.IOptions, context: ComponentMetadata,
templateStart: number, expressionVisitorCtrl: RecursiveAngularExpressionVisitorCtr);
templateStart: number, expressionVisitorCtrl: RecursiveAngularExpressionVisitorCtr, languageService?: ts.LanguageService);
}

export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast.TemplateAstVisitor {
Expand All @@ -80,13 +81,14 @@ export class BasicTemplateAstVisitor extends SourceMappingVisitor implements ast
private _originalOptions: Lint.IOptions,
protected context: ComponentMetadata,
protected templateStart: number,
private expressionVisitorCtrl: RecursiveAngularExpressionVisitorCtr = RecursiveAngularExpressionVisitor) {
private expressionVisitorCtrl: RecursiveAngularExpressionVisitorCtr = RecursiveAngularExpressionVisitor,
protected languageService?: ts.LanguageService) {
super(sourceFile, _originalOptions, context.template.template, templateStart);
}

protected visitNg2TemplateAST(ast: e.AST, templateStart: number) {
const templateVisitor =
new this.expressionVisitorCtrl(this.getSourceFile(), this._originalOptions, this.context, templateStart);
new this.expressionVisitorCtrl(this.getSourceFile(), this._originalOptions, this.context, templateStart, this.languageService);
templateVisitor.preDefinedVariables = this._variables;
templateVisitor.visit(ast);
templateVisitor.getFailures().forEach(f => this.addFailure(f));
Expand Down
2 changes: 1 addition & 1 deletion src/angular/templates/recursiveAngularExpressionVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class RecursiveAngularExpressionVisitor extends SourceMappingVisitor impl
public preDefinedVariables = [];

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions,
protected context: ComponentMetadata, protected basePosition: number) {
protected context: ComponentMetadata, protected basePosition: number, protected languageService: ts.LanguageService) {
super(sourceFile, options, context.template.template, basePosition);
}

Expand Down
28 changes: 20 additions & 8 deletions src/noAccessMissingMemberRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {stringDistance} from './util/utils';
import {Ng2Walker} from './angular/ng2Walker';
import {RecursiveAngularExpressionVisitor} from './angular/templates/recursiveAngularExpressionVisitor';
import {ExpTypes} from './angular/expressionTypes';
import {getDeclaredMethodNames, getDeclaredPropertyNames} from './util/classDeclarationUtils';
import {getClassMembers} from './util/classDeclarationUtils';
import * as e from '@angular/compiler/src/expression_parser/ast';

import {Config} from './angular/config';
Expand Down Expand Up @@ -44,8 +44,10 @@ class SymbolAccessValidator extends RecursiveAngularExpressionVisitor {
symbolType = 'property';
}

available = getDeclaredMethodNames(this.context.controller)
.concat(getDeclaredPropertyNames(this.context.controller))
const typeChecker = this.languageService.getProgram().getTypeChecker();

available = getClassMembers(this.context.controller, typeChecker)
.map(p => p.name)
.concat(this.preDefinedVariables);

// Do not support nested properties yet
Expand Down Expand Up @@ -122,14 +124,24 @@ class SymbolAccessValidator extends RecursiveAngularExpressionVisitor {
}
}

export class Rule extends Lint.Rules.AbstractRule {
static FAILURE: string = 'The %s "%s" that you\'re trying to access does not exist in the class declaration.';
export class Rule extends Lint.Rules.TypedRule {
public static FAILURE: string = 'The %s "%s" that you\'re trying to access does not exist in the class declaration.';
public static metadata: Lint.IRuleMetadata = {
ruleName: 'no-access-missing-member',
description: 'Prevents bindings to expressions containing non-existing methods or properties',
optionsDescription: 'Not configurable',
options: null,
type: 'functionality',
typescriptOnly: true
};

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
}));
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/util/classDeclarationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { current } from './syntaxKind';

const SyntaxKind = current();

export const getClassMembers = (declaration: ts.ClassDeclaration, tc: ts.TypeChecker): ts.Symbol[] => {
const properties = tc.getTypeAtLocation(declaration).getProperties();
return properties;
};

export const getDeclaredProperties = (declaration: ts.ClassDeclaration) => {
const m = declaration.members;
const ctr = m.filter((m: any) => m.kind === SyntaxKind.Constructor).pop();
Expand Down
Loading

0 comments on commit d23d83c

Please sign in to comment.