Skip to content

Commit

Permalink
fix(compiler-cli): report cases where initializer APIs are used in a …
Browse files Browse the repository at this point in the history
…non-directive class (#54993)

Expands the check for initializer APIs to also flag when the function is called on a class that isn't a component or directive.

PR Close #54993
  • Loading branch information
crisbeto authored and dylhunn committed Mar 28, 2024
1 parent 78188e8 commit 694ba79
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class InitializerApiUsageRule implements SourceFileValidatorRule {
});
}

checkNode(node: ts.Node): ts.Diagnostic[]|null {
checkNode(node: ts.Node): ts.Diagnostic|null {
// We only care about call expressions.
if (!ts.isCallExpression(node)) {
return null;
Expand All @@ -50,9 +50,7 @@ export class InitializerApiUsageRule implements SourceFileValidatorRule {
node = node.parent;
}

// Initializer functions are allowed to be used in the initializer.
if (!node.parent || !ts.isCallExpression(node) ||
(ts.isPropertyDeclaration(node.parent) && node.parent.initializer === node)) {
if (!node.parent || !ts.isCallExpression(node)) {
return null;
}

Expand All @@ -62,13 +60,36 @@ export class InitializerApiUsageRule implements SourceFileValidatorRule {
return null;
}

return [
makeDiagnostic(
ErrorCode.UNSUPPORTED_INITIALIZER_API_USAGE, node,
`Unsupported call to the ${identifiedInitializer.api.functionName}${
identifiedInitializer.isRequired ?
'.required' :
''} function. This function can only be called in the initializer of a class member.`)
];
const functionName = identifiedInitializer.api.functionName +
(identifiedInitializer.isRequired ? '.required' : '');

if (ts.isPropertyDeclaration(node.parent) && node.parent.initializer === node) {
let closestClass: ts.Node = node.parent;

while (closestClass && !ts.isClassDeclaration(closestClass)) {
closestClass = closestClass.parent;
}

if (closestClass && ts.isClassDeclaration(closestClass)) {
const decorators = this.reflector.getDecoratorsOfDeclaration(closestClass);
const isComponentOrDirective = decorators !== null && decorators.some(decorator => {
return decorator.import?.from === '@angular/core' &&
(decorator.name === 'Component' || decorator.name === 'Directive');
});

return isComponentOrDirective ?
null :
makeDiagnostic(
ErrorCode.UNSUPPORTED_INITIALIZER_API_USAGE, node,
`Unsupported call to the ${
functionName} function. This function can only be used as the initializer ` +
`of a property on a @Component or @Directive class.`);
}
}

return makeDiagnostic(
ErrorCode.UNSUPPORTED_INITIALIZER_API_USAGE, node,
`Unsupported call to the ${
functionName} function. This function can only be called in the initializer of a class member.`);
}
}
33 changes: 33 additions & 0 deletions packages/compiler-cli/test/ngtsc/authoring_diagnostics_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,38 @@ runInEachFileSystem(() => {
const diags = env.driveDiagnostics();
expect(diags.length).toBe(0);
});

it('should report initializer function being used in an undecorated class', () => {
env.write('test.ts', `
import {input} from '@angular/core';
export class Test {
inp = input();
}
`);

const diags = env.driveDiagnostics();
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toContain(
'Unsupported call to the input function. This function can only be used as the initializer of a property on a @Component or @Directive class.');
});

it('should report initializer function being used in an unsupported Angular class', () => {
env.write('test.ts', `
import {input, Pipe} from '@angular/core';
@Pipe({name: 'test'})
export class Test {
inp = input();
}
`);

const diags = env.driveDiagnostics();
expect(diags.length).toBe(1);
expect(diags[0].messageText)
.toContain(
'Unsupported call to the input function. This function can only be used as the initializer of a property on a @Component or @Directive class.');
});
});
});

0 comments on commit 694ba79

Please sign in to comment.