@@ -347,33 +347,11 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
347347 isComponent ? 'component' : 'directive'
348348 } ${ directiveName } must be specified.`;
349349
350- let span : ParseSourceSpan ;
351- let name : string | null ;
352-
353- if ( element instanceof TmplAstElement || element instanceof TmplAstDirective ) {
354- name = element . name ;
355- } else if ( element instanceof TmplAstComponent ) {
356- name = element . componentName ;
357- } else {
358- name = null ;
359- }
360-
361- if ( name === null ) {
362- span = element . startSourceSpan ;
363- } else {
364- // Only highlight the tag name since highlighting the entire start tag can be noisy.
365- const start = element . startSourceSpan . start . moveBy ( 1 ) ;
366- const end = element . startSourceSpan . end . moveBy (
367- start . offset + name . length - element . startSourceSpan . end . offset ,
368- ) ;
369- span = new ParseSourceSpan ( start , end ) ;
370- }
371-
372350 this . _diagnostics . push (
373351 makeTemplateDiagnostic (
374352 id ,
375353 this . resolver . getTemplateSourceMapping ( id ) ,
376- span ,
354+ this . getTagNameSpan ( element ) ,
377355 ts . DiagnosticCategory . Error ,
378356 ngErrorCode ( ErrorCode . MISSING_REQUIRED_INPUTS ) ,
379357 message ,
@@ -694,6 +672,59 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
694672 ) ,
695673 ) ;
696674 }
675+
676+ conflictingHostDirectiveBinding (
677+ id : TypeCheckId ,
678+ node : TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective ,
679+ directiveName : string ,
680+ kind : 'input' | 'output' ,
681+ classPropertyName : string ,
682+ aliases : string [ ] ,
683+ ) : void {
684+ const message =
685+ `${ kind === 'input' ? 'Input' : 'Output' } declared in ${ directiveName } .${ classPropertyName } ` +
686+ `is exposed under the following conflicting names: ${ aliases . map ( ( a ) => `"${ a } "` ) . join ( ', ' ) } . ` +
687+ `An ${ kind } can only be exposed under a single name.` ;
688+
689+ this . _diagnostics . push (
690+ makeTemplateDiagnostic (
691+ id ,
692+ this . resolver . getTemplateSourceMapping ( id ) ,
693+ this . getTagNameSpan ( node ) ,
694+ ts . DiagnosticCategory . Error ,
695+ ngErrorCode ( ErrorCode . CONFLICTING_HOST_DIRECTIVE_BINDING ) ,
696+ message ,
697+ ) ,
698+ ) ;
699+ }
700+
701+ private getTagNameSpan (
702+ node : TmplAstElement | TmplAstTemplate | TmplAstComponent | TmplAstDirective ,
703+ ) {
704+ let span : ParseSourceSpan ;
705+ let name : string | null ;
706+
707+ if ( node instanceof TmplAstElement || node instanceof TmplAstDirective ) {
708+ name = node . name ;
709+ } else if ( node instanceof TmplAstComponent ) {
710+ name = node . componentName ;
711+ } else {
712+ name = null ;
713+ }
714+
715+ if ( name === null ) {
716+ span = node . startSourceSpan ;
717+ } else {
718+ // Only highlight the tag name since highlighting the entire start tag can be noisy.
719+ const start = node . startSourceSpan . start . moveBy ( 1 ) ;
720+ const end = node . startSourceSpan . end . moveBy (
721+ start . offset + name . length - node . startSourceSpan . end . offset ,
722+ ) ;
723+ span = new ParseSourceSpan ( start , end ) ;
724+ }
725+
726+ return span ;
727+ }
697728}
698729
699730function translateCategory ( category : OutOfBadDiagnosticCategory ) : ts . DiagnosticCategory {
0 commit comments