mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(compiler-cli): add NullishCoalescingNotNullableCheck (#43232)
Add a template check that returns diagnostics if the left side of a nullish coalescing operation is not nullable. Refs #42966 PR Close #43232
This commit is contained in:
committed by
Jessica Janiuk
parent
fc85fa85ba
commit
0802b59a7b
@@ -40,6 +40,7 @@ ts_library(
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/extended",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/checks/invalid_banana_in_box",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/checks/nullish_coalescing_not_nullable",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
"//packages/compiler-cli/src/ngtsc/xi18n",
|
||||
"@npm//typescript",
|
||||
|
||||
@@ -31,8 +31,9 @@ import {aliasTransformFactory, CompilationMode, declarationTransformFactory, Dec
|
||||
import {TemplateTypeCheckerImpl} from '../../typecheck';
|
||||
import {OptimizeFor, TemplateTypeChecker, TypeCheckingConfig} from '../../typecheck/api';
|
||||
import {ExtendedTemplateCheckerImpl} from '../../typecheck/extended';
|
||||
import {ExtendedTemplateChecker} from '../../typecheck/extended/api';
|
||||
import {ExtendedTemplateChecker, TemplateCheck} from '../../typecheck/extended/api';
|
||||
import {InvalidBananaInBoxCheck} from '../../typecheck/extended/checks/invalid_banana_in_box';
|
||||
import {NullishCoalescingNotNullableCheck} from '../../typecheck/extended/checks/nullish_coalescing_not_nullable';
|
||||
import {getSourceFileOrNull, isDtsPath, resolveModuleName, toUnredirectedSourceFile} from '../../util/src/typescript';
|
||||
import {Xi18nContext} from '../../xi18n';
|
||||
import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api';
|
||||
@@ -1109,7 +1110,10 @@ export class NgCompiler {
|
||||
reflector, this.adapter, this.incrementalCompilation, scopeRegistry, typeCheckScopeRegistry,
|
||||
this.delegatingPerfRecorder);
|
||||
|
||||
const templateChecks = [new InvalidBananaInBoxCheck()];
|
||||
const templateChecks: TemplateCheck<ErrorCode>[] = [new InvalidBananaInBoxCheck()];
|
||||
if (this.options.strictNullChecks) {
|
||||
templateChecks.push(new NullishCoalescingNotNullableCheck());
|
||||
}
|
||||
const extendedTemplateChecker =
|
||||
new ExtendedTemplateCheckerImpl(templateTypeChecker, checker, templateChecks);
|
||||
|
||||
|
||||
@@ -182,6 +182,16 @@ export enum ErrorCode {
|
||||
*/
|
||||
INVALID_BANANA_IN_BOX = 8101,
|
||||
|
||||
/**
|
||||
* The left side of a nullish coalescing operation is not nullable.
|
||||
*
|
||||
* ```
|
||||
* {{ foo ?? bar }}
|
||||
* ```
|
||||
* When the type of foo doesn't include `null` or `undefined`.
|
||||
*/
|
||||
NULLISH_COALESCING_NOT_NULLABLE = 8102,
|
||||
|
||||
/**
|
||||
* The template type-checking engine would need to generate an inline type check block for a
|
||||
* component, but the current type-checking environment doesn't support it.
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "nullish_coalescing_not_nullable",
|
||||
srcs = ["index.ts"],
|
||||
visibility = ["//packages/compiler-cli/src/ngtsc:__subpackages__"],
|
||||
deps = [
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/api",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, Binary, TmplAstNode} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode} from '../../../../diagnostics';
|
||||
import {NgTemplateDiagnostic, SymbolKind} from '../../../api';
|
||||
import {TemplateCheckWithVisitor, TemplateContext} from '../../api';
|
||||
|
||||
/**
|
||||
* Ensures the left side of a nullish coalescing operation is nullable.
|
||||
* Returns diagnostics for the cases where the operator is useless.
|
||||
* This check should only be use if `strictNullChecks` is enabled,
|
||||
* otherwise it would produce inaccurate results.
|
||||
*/
|
||||
export class NullishCoalescingNotNullableCheck extends
|
||||
TemplateCheckWithVisitor<ErrorCode.NULLISH_COALESCING_NOT_NULLABLE> {
|
||||
override code = ErrorCode.NULLISH_COALESCING_NOT_NULLABLE as const;
|
||||
|
||||
override visitNode(ctx: TemplateContext, component: ts.ClassDeclaration, node: TmplAstNode|AST):
|
||||
NgTemplateDiagnostic<ErrorCode.NULLISH_COALESCING_NOT_NULLABLE>[] {
|
||||
if (!(node instanceof Binary) || node.operation !== '??') return [];
|
||||
|
||||
const symbolLeft = ctx.templateTypeChecker.getSymbolOfNode(node.left, component)!;
|
||||
if (symbolLeft.kind !== SymbolKind.Expression) {
|
||||
return [];
|
||||
}
|
||||
const typeLeft = symbolLeft.tsType;
|
||||
// If the left operand's type is different from its non-nullable self, then it must
|
||||
// contain a null or undefined so this nullish coalescing operator is useful. No diagnostic to
|
||||
// report.
|
||||
if (typeLeft.getNonNullableType() !== typeLeft) return [];
|
||||
|
||||
const symbol = ctx.templateTypeChecker.getSymbolOfNode(node, component)!;
|
||||
if (symbol.kind !== SymbolKind.Expression) {
|
||||
return [];
|
||||
}
|
||||
const span =
|
||||
ctx.templateTypeChecker.getTemplateMappingAtShimLocation(symbol.shimLocation)!.span;
|
||||
const diagnostic = ctx.templateTypeChecker.makeTemplateDiagnostic(
|
||||
component, span, ts.DiagnosticCategory.Warning, ErrorCode.NULLISH_COALESCING_NOT_NULLABLE,
|
||||
`The left side of this nullish coalescing operation does not include 'null' or 'undefined' in its type, therefore the '??' operator can be safely removed.`);
|
||||
return [diagnostic];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user