Skip to content

Commit e89ebf3

Browse files
crisbetommalerba
authored andcommitted
refactor(compiler-cli): add infrastructure for new diagnostics (angular#60977)
We need a couple of custom diagnostics for selectorless. These changes add the infrastructure so they can be reported. PR Close angular#60977
1 parent bc9a067 commit e89ebf3

File tree

4 files changed

+123
-2
lines changed

4 files changed

+123
-2
lines changed

goldens/public-api/compiler-cli/error_code.api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export enum ErrorCode {
5858
IMPORT_CYCLE_DETECTED = 3003,
5959
IMPORT_GENERATION_FAILURE = 3004,
6060
INACCESSIBLE_DEFERRED_TRIGGER_ELEMENT = 8010,
61+
INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE = 2025,
6162
INCORRECTLY_DECLARED_ON_STATIC_MEMBER = 1100,
6263
INITIALIZER_API_DECORATOR_METADATA_COLLISION = 1051,
6364
INITIALIZER_API_DISALLOWED_MEMBER_VISIBILITY = 1053,
@@ -73,6 +74,7 @@ export enum ErrorCode {
7374
LOCAL_COMPILATION_UNRESOLVED_CONST = 11001,
7475
LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION = 11003,
7576
MISSING_CONTROL_FLOW_DIRECTIVE = 8103,
77+
MISSING_NAMED_TEMPLATE_DEPENDENCY = 2024,
7678
MISSING_NGFOROF_LET = 8105,
7779
MISSING_PIPE = 8004,
7880
MISSING_REFERENCE_TARGET = 8003,
@@ -104,6 +106,7 @@ export enum ErrorCode {
104106
SYMBOL_NOT_EXPORTED = 3001,
105107
TEMPLATE_PARSE_ERROR = 5002,
106108
TEXT_ATTRIBUTE_NOT_BINDING = 8104,
109+
UNCLAIMED_DIRECTIVE_BINDING = 8018,
107110
UNDECORATED_CLASS_USING_ANGULAR_FEATURES = 2007,
108111
UNDECORATED_PROVIDER = 2005,
109112
UNINVOKED_FUNCTION_IN_EVENT_BINDING = 8111,

packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ export enum ErrorCode {
162162
*/
163163
NON_STANDALONE_NOT_ALLOWED = 2023,
164164

165+
/**
166+
* Raised when a named template dependency isn't defined in the component's source file.
167+
*/
168+
MISSING_NAMED_TEMPLATE_DEPENDENCY = 2024,
169+
170+
/**
171+
* Raised if an incorrect type is used for a named template dependency (e.g. directive
172+
* class used as a component).
173+
*/
174+
INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE = 2025,
175+
165176
SYMBOL_NOT_EXPORTED = 3001,
166177
/**
167178
* Raised when a relationship between directives and/or pipes would cause a cyclic import to be
@@ -374,6 +385,12 @@ export enum ErrorCode {
374385
/** A `@let` declaration conflicts with another symbol in the same scope. */
375386
CONFLICTING_LET_DECLARATION = 8017,
376387

388+
/**
389+
* A binding inside selectorless directive syntax did
390+
* not match any inputs/outputs of the directive.
391+
*/
392+
UNCLAIMED_DIRECTIVE_BINDING = 8018,
393+
377394
/**
378395
* A two way binding in a template has an incorrect syntax,
379396
* parentheses outside brackets. For example:

packages/compiler-cli/src/ngtsc/typecheck/src/oob.ts

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
PropertyWrite,
1414
TmplAstBoundAttribute,
1515
TmplAstBoundEvent,
16+
TmplAstComponent,
17+
TmplAstDirective,
1618
TmplAstElement,
1719
TmplAstForLoopBlock,
1820
TmplAstForLoopBlockEmpty,
@@ -23,6 +25,7 @@ import {
2325
TmplAstReference,
2426
TmplAstSwitchBlockCase,
2527
TmplAstTemplate,
28+
TmplAstTextAttribute,
2629
TmplAstVariable,
2730
TmplAstViewportDeferredTrigger,
2831
} from '@angular/compiler';
@@ -125,7 +128,7 @@ export interface OutOfBandDiagnosticRecorder {
125128
/** Reports required inputs that haven't been bound. */
126129
missingRequiredInputs(
127130
id: TypeCheckId,
128-
element: TmplAstElement | TmplAstTemplate,
131+
element: TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective,
129132
directiveName: string,
130133
isComponent: boolean,
131134
inputAliases: string[],
@@ -185,6 +188,34 @@ export interface OutOfBandDiagnosticRecorder {
185188
* @param current the `TmplAstLetDeclaration` which is invalid.
186189
*/
187190
conflictingDeclaration(id: TypeCheckId, current: TmplAstLetDeclaration): void;
191+
192+
/**
193+
* Reports that a named template dependency (e.g. `<Missing/>`) is not available.
194+
* @param id Type checking ID of the template in which the dependency is declared.
195+
* @param node Node that declares the dependency.
196+
*/
197+
missingNamedTemplateDependency(id: TypeCheckId, node: TmplAstComponent | TmplAstDirective): void;
198+
199+
/**
200+
* Reports that a templace dependency of the wrong kind has been referenced at a specific position
201+
* (e.g. `<SomeDirective/>`).
202+
* @param id Type checking ID of the template in which the dependency is declared.
203+
* @param node Node that declares the dependency.
204+
*/
205+
incorrectTemplateDependencyType(id: TypeCheckId, node: TmplAstComponent | TmplAstDirective): void;
206+
207+
/**
208+
* Reports a binding inside directive syntax that does not match any of the inputs/outputs of
209+
* the directive.
210+
* @param id Type checking ID of the template in which the directive was defined.
211+
* @param directive Directive that contains the binding.
212+
* @param node Node declaring the binding.
213+
*/
214+
unclaimedDirectiveBinding(
215+
id: TypeCheckId,
216+
directive: TmplAstDirective,
217+
node: TmplAstBoundAttribute | TmplAstTextAttribute | TmplAstBoundEvent,
218+
): void;
188219
}
189220

190221
export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecorder {
@@ -467,7 +498,7 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
467498

468499
missingRequiredInputs(
469500
id: TypeCheckId,
470-
element: TmplAstElement | TmplAstTemplate,
501+
element: TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective,
471502
directiveName: string,
472503
isComponent: boolean,
473504
inputAliases: string[],
@@ -656,6 +687,58 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
656687
),
657688
);
658689
}
690+
691+
missingNamedTemplateDependency(id: TypeCheckId, node: TmplAstComponent | TmplAstDirective): void {
692+
this._diagnostics.push(
693+
makeTemplateDiagnostic(
694+
id,
695+
this.resolver.getTemplateSourceMapping(id),
696+
node.startSourceSpan,
697+
ts.DiagnosticCategory.Error,
698+
ngErrorCode(ErrorCode.MISSING_NAMED_TEMPLATE_DEPENDENCY),
699+
// Wording is meant to mimic the wording TS uses in their diagnostic for missing symbols.
700+
`Cannot find name "${node instanceof TmplAstDirective ? node.name : node.componentName}".`,
701+
),
702+
);
703+
}
704+
705+
incorrectTemplateDependencyType(
706+
id: TypeCheckId,
707+
node: TmplAstComponent | TmplAstDirective,
708+
): void {
709+
this._diagnostics.push(
710+
makeTemplateDiagnostic(
711+
id,
712+
this.resolver.getTemplateSourceMapping(id),
713+
node.startSourceSpan,
714+
ts.DiagnosticCategory.Error,
715+
ngErrorCode(ErrorCode.INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE),
716+
`Incorrect reference type. Type must be an ${node instanceof TmplAstComponent ? '@Component' : '@Directive'}.`,
717+
),
718+
);
719+
}
720+
721+
unclaimedDirectiveBinding(
722+
id: TypeCheckId,
723+
directive: TmplAstDirective,
724+
node: TmplAstBoundAttribute | TmplAstTextAttribute | TmplAstBoundEvent,
725+
): void {
726+
const errorMsg =
727+
`Directive ${directive.name} does not have an ` +
728+
`${node instanceof TmplAstBoundEvent ? 'output' : 'input'} named "${node.name}". ` +
729+
`Bindings to directives must target existing inputs or outputs.`;
730+
731+
this._diagnostics.push(
732+
makeTemplateDiagnostic(
733+
id,
734+
this.resolver.getTemplateSourceMapping(id),
735+
node.keySpan || node.sourceSpan,
736+
ts.DiagnosticCategory.Error,
737+
ngErrorCode(ErrorCode.UNCLAIMED_DIRECTIVE_BINDING),
738+
errorMsg,
739+
),
740+
);
741+
}
659742
}
660743

661744
function makeInlineDiagnostic(

packages/compiler-cli/src/ngtsc/typecheck/testing/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ import {
1717
R3TargetBinder,
1818
SelectorlessMatcher,
1919
SelectorMatcher,
20+
TmplAstBoundAttribute,
21+
TmplAstBoundEvent,
22+
TmplAstComponent,
23+
TmplAstDirective,
2024
TmplAstElement,
2125
TmplAstLetDeclaration,
26+
TmplAstTextAttribute,
2227
} from '@angular/compiler';
2328
import {readFileSync} from 'fs';
2429
import path from 'path';
@@ -1023,4 +1028,17 @@ export class NoopOobRecorder implements OutOfBandDiagnosticRecorder {
10231028
target: TmplAstLetDeclaration,
10241029
): void {}
10251030
conflictingDeclaration(id: TypeCheckId, current: TmplAstLetDeclaration): void {}
1031+
missingNamedTemplateDependency(
1032+
id: TypeCheckId,
1033+
node: TmplAstComponent | TmplAstDirective,
1034+
): void {}
1035+
unclaimedDirectiveBinding(
1036+
id: TypeCheckId,
1037+
directive: TmplAstDirective,
1038+
node: TmplAstBoundAttribute | TmplAstTextAttribute | TmplAstBoundEvent,
1039+
): void {}
1040+
incorrectTemplateDependencyType(
1041+
id: TypeCheckId,
1042+
node: TmplAstComponent | TmplAstDirective,
1043+
): void {}
10261044
}

0 commit comments

Comments
 (0)