Host bindings to the `class` property crash the compiler if the binding expression is empty

🐞 bug report

Affected Package

The issue is caused by package @angular/compiler.

Is this a regression?

NA

Description

An old Angular enterprise application that was based on v6.0.3, is migrated successfully to v11.0.6 and then Ivy is enabled using "enableIvy": true in tsconfig.lib.json.

But, on ng build --prod getting the below mentioned error.

I tried going the file and line specified in the error i.e. compiler.umd.js:9036:52 and found the following lines of code:

/**
     * Converts the given expression AST into an executable output AST, assuming the expression
     * is used in property binding. The expression has to be preprocessed via
     * `convertPropertyBindingBuiltins`.
     */
    function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
        if (!localResolver) {
            localResolver = new DefaultLocalResolver();
        }
        var visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
        console.log(expressionWithoutBuiltins, '\n\n', localResolver, '\n\n', implicitReceiver, '\n\n', bindingId, '\n\n', form, '\n\n', interpolationFunction.toString(), '\n\n', visitor);
        console.log('********************************************');
        var outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
        console.log('********' + JSON.stringify(outputExpr) + '************************************');
        var stmts = getStatementsFromVisitor(visitor, bindingId);
        ...
        ...
        ...

The above console.logs, give the following output:

Compiling @my-scope/project : es2015 as esm2015
LiteralPrimitive {
  span: ParseSpan { start: 0, end: 4 },
  sourceSpan: AbsoluteSourceSpan { start: 28071, end: 28075 },
  value: true
}

 DefaultLocalResolver { globals: undefined }

 ReadVarExpr {
  type: null,
  sourceSpan: null,
  name: 'ctx',
  builtin: null
}

 b

 2

 function () { return error('Unexpected interpolation'); }

 _AstToIrVisitor {
  _localResolver: DefaultLocalResolver { globals: undefined },
  _implicitReceiver: ReadVarExpr {
    type: null,
    sourceSpan: null,
    name: 'ctx',
    builtin: null
  },
  bindingId: 'b',
  interpolationFunction: [Function (anonymous)],
  baseSourceSpan: undefined,
  implicitReceiverAccesses: undefined,
  _nodeMap: Map(0) {},
  _resultMap: Map(0) {},
  _currentTemporary: 0,
  temporaryCount: 0,
  usesImplicitReceiver: false
}
********************************************
********{"type":{"modifiers":[],"name":6},"sourceSpan":null,"value":true}************************************
undefined

 DefaultLocalResolver { globals: undefined }

 ReadVarExpr {
  type: null,
  sourceSpan: null,
  name: 'ctx',
  builtin: null
}

 b

 2

 function () { return error('Unexpected interpolation'); }

 _AstToIrVisitor {
  _localResolver: DefaultLocalResolver { globals: undefined },
  _implicitReceiver: ReadVarExpr {
    type: null,
    sourceSpan: null,
    name: 'ctx',
    builtin: null
  },
  bindingId: 'b',
  interpolationFunction: [Function (anonymous)],
  baseSourceSpan: undefined,
  implicitReceiverAccesses: undefined,
  _nodeMap: Map(0) {},
  _resultMap: Map(0) {},
  _currentTemporary: 0,
  temporaryCount: 0,
  usesImplicitReceiver: false
}
********************************************

The component that causes the crash uses a host binding to the class property where the binding expression is left empty:

const MY_BUTTON_CLASS_NAME = 'my-btn';

@Directive({
    selector: 'button[myButton], a[myButton]',
    host: {
        'class': MY_BUTTON_CLASS_NAME,
        '[class]': ''
    },
    exportAs: 'mmButton'
})
export class MyButtonDirective implements OnChanges { ... }

🔬 Minimal Reproduction

Hard to reproduce as this error is appearing in an old enterprise Angular library project that is recently migrated from v6.0.3 to v11.0.6.

🔥 Exception or Error


Error: Error on worker #1: TypeError: Cannot read property 'visit' of undefined
    at convertPropertyBinding (node_modules\@angular\compiler\bundles\compiler.umd.js:9036:52)
    at bindingFn (node_modules\@angular\compiler\bundles\compiler.umd.js:20733:16)
    at node_modules\@angular\compiler\bundles\compiler.umd.js:20643:31
    at Array.forEach ()
    at createHostBindingsFunction (node_modules\@angular\compiler\bundles\compiler.umd.js:20640:26)
    at baseDirectiveFields (node_modules\@angular\compiler\bundles\compiler.umd.js:20208:43)
    at Object.compileDirectiveFromMetadata (node_modules\@angular\compiler\bundles\compiler.umd.js:20250:29)
    at DirectiveDecoratorHandler.compileFull (node_modules\@angular\compiler-cli\src\ngtsc\annotations\src\directive.js:126:34)
    at _loop_2 (node_modules\@angular\compiler-cli\src\ngtsc\transform\src\compilation.js:552:39)
    at NgccTraitCompiler.TraitCompiler.compile (node_modules\@angular\compiler-cli\src\ngtsc\transform\src\compilation.js:584:21)
    at ClusterMaster.onWorkerMessage (node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:195:27)
    at node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:55:95
    at ClusterMaster. (node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:293:57)
    at step (node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:143:27)
    at Object.next (node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:124:57)
    at node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:117:75
    at new Promise ()
    at Object.__awaiter (node_modules\@angular\compiler-cli\node_modules\tslib\tslib.js:113:16)
    at EventEmitter. (node_modules\@angular\compiler-cli\ngcc\src\execution\cluster\master.js:287:32)
    at EventEmitter.emit (events.js:315:20)
An unhandled exception occurred: NGCC failed.

🌍 Your Environment

Angular Version:




     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 11.0.6
Node: 14.15.4
OS: win32 x64

Angular: 11.0.6
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1100.6
@angular-devkit/build-angular   0.1100.6
@angular-devkit/core            11.0.6
@angular-devkit/schematics      11.0.6
@schematics/angular             11.0.6
@schematics/update              0.1100.6
ng-packagr                      11.0.0
rxjs                            6.6.3
typescript                      4.0.5


Anything else relevant?

tsconfig.lib.prod.json

{
    "extends": "./tsconfig.lib.json",
    "compilerOptions": {
        "declarationMap": false
    },
    "angularCompilerOptions": {
        "enableIvy": true
    }
}

tsconfig.lib.json

{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "outDir": "../../out-tsc/lib",
        "declarationMap": true,
        "target": "es2015",
        "declaration": true,
        "inlineSources": true,
        "types": [],
        "lib": [
            "dom",
            "es2015"
        ],
        "paths": {
            "@assets*": ["src/lib/assets/*"]
        }
    },
    "angularCompilerOptions": {
        "skipTemplateCodegen": true,
        "strictMetadataEmit": true,
        "enableResourceInlining": true
    },
    "exclude": [
        "src/test.ts",
        "**/*.spec.ts"
    ]
}

1 possible answer(s) on “Host bindings to the `class` property crash the compiler if the binding expression is empty

  1. If the library and the application are both internal, and you ensure that both are always built with the exact same version of Angular then B.