mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-29 16:29:19 -05:00
Report grammar errors for invalid decorator grammar (#57749)
This commit is contained in:
@@ -651,6 +651,7 @@ import {
|
||||
isNewExpression,
|
||||
isNodeDescendantOf,
|
||||
isNonNullAccess,
|
||||
isNonNullExpression,
|
||||
isNullishCoalesce,
|
||||
isNumericLiteral,
|
||||
isNumericLiteralName,
|
||||
@@ -41572,8 +41573,82 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
function checkGrammarDecorator(decorator: Decorator): boolean {
|
||||
const sourceFile = getSourceFileOfNode(decorator);
|
||||
if (!hasParseDiagnostics(sourceFile)) {
|
||||
let node: Expression = decorator.expression;
|
||||
|
||||
// DecoratorParenthesizedExpression :
|
||||
// `(` Expression `)`
|
||||
|
||||
if (isParenthesizedExpression(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let canHaveCallExpression = true;
|
||||
let errorNode: Node | undefined;
|
||||
while (true) {
|
||||
// Allow TS syntax such as non-null assertions and instantiation expressions
|
||||
if (isExpressionWithTypeArguments(node) || isNonNullExpression(node)) {
|
||||
node = node.expression;
|
||||
continue;
|
||||
}
|
||||
|
||||
// DecoratorCallExpression :
|
||||
// DecoratorMemberExpression Arguments
|
||||
|
||||
if (isCallExpression(node)) {
|
||||
if (!canHaveCallExpression) {
|
||||
errorNode = node;
|
||||
}
|
||||
if (node.questionDotToken) {
|
||||
// Even if we already have an error node, error at the `?.` token since it appears earlier.
|
||||
errorNode = node.questionDotToken;
|
||||
}
|
||||
node = node.expression;
|
||||
canHaveCallExpression = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// DecoratorMemberExpression :
|
||||
// IdentifierReference
|
||||
// DecoratorMemberExpression `.` IdentifierName
|
||||
// DecoratorMemberExpression `.` PrivateIdentifier
|
||||
|
||||
if (isPropertyAccessExpression(node)) {
|
||||
if (node.questionDotToken) {
|
||||
// Even if we already have an error node, error at the `?.` token since it appears earlier.
|
||||
errorNode = node.questionDotToken;
|
||||
}
|
||||
node = node.expression;
|
||||
canHaveCallExpression = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isIdentifier(node)) {
|
||||
// Even if we already have an error node, error at this node since it appears earlier.
|
||||
errorNode = node;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (errorNode) {
|
||||
addRelatedInfo(
|
||||
error(decorator.expression, Diagnostics.Expression_must_be_enclosed_in_parentheses_to_be_used_as_a_decorator),
|
||||
createDiagnosticForNode(errorNode, Diagnostics.Invalid_syntax_in_decorator),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check a decorator */
|
||||
function checkDecorator(node: Decorator): void {
|
||||
checkGrammarDecorator(node);
|
||||
|
||||
const signature = getResolvedSignature(node);
|
||||
checkDeprecatedSignature(signature, node);
|
||||
const returnType = getReturnTypeOfSignature(signature);
|
||||
|
||||
@@ -1637,6 +1637,14 @@
|
||||
"category": "Error",
|
||||
"code": 1496
|
||||
},
|
||||
"Expression must be enclosed in parentheses to be used as a decorator.": {
|
||||
"category": "Error",
|
||||
"code": 1497
|
||||
},
|
||||
"Invalid syntax in decorator.": {
|
||||
"category": "Error",
|
||||
"code": 1498
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
@@ -7792,6 +7800,14 @@
|
||||
"category": "Message",
|
||||
"code": 95193
|
||||
},
|
||||
"Wrap in parentheses": {
|
||||
"category": "Message",
|
||||
"code": 95194
|
||||
},
|
||||
"Wrap all invalid decorator expressions in parentheses": {
|
||||
"category": "Message",
|
||||
"code": 95195
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -64,6 +64,7 @@ export * from "../codefixes/useDefaultImport";
|
||||
export * from "../codefixes/useBigintLiteral";
|
||||
export * from "../codefixes/fixAddModuleReferTypeMissingTypeof";
|
||||
export * from "../codefixes/wrapJsxInFragment";
|
||||
export * from "../codefixes/wrapDecoratorInParentheses";
|
||||
export * from "../codefixes/convertToMappedObjectType";
|
||||
export * from "../codefixes/removeAccidentalCallParentheses";
|
||||
export * from "../codefixes/removeUnnecessaryAwait";
|
||||
|
||||
35
src/services/codefixes/wrapDecoratorInParentheses.ts
Normal file
35
src/services/codefixes/wrapDecoratorInParentheses.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
Debug,
|
||||
Diagnostics,
|
||||
factory,
|
||||
findAncestor,
|
||||
getTokenAtPosition,
|
||||
isDecorator,
|
||||
SourceFile,
|
||||
textChanges,
|
||||
} from "../_namespaces/ts";
|
||||
import {
|
||||
codeFixAll,
|
||||
createCodeFixAction,
|
||||
registerCodeFix,
|
||||
} from "../_namespaces/ts.codefix";
|
||||
|
||||
const fixId = "wrapDecoratorInParentheses";
|
||||
const errorCodes = [Diagnostics.Expression_must_be_enclosed_in_parentheses_to_be_used_as_a_decorator.code];
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions: function getCodeActionsToWrapDecoratorExpressionInParentheses(context) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start));
|
||||
return [createCodeFixAction(fixId, changes, Diagnostics.Wrap_in_parentheses, fixId, Diagnostics.Wrap_all_invalid_decorator_expressions_in_parentheses)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start)),
|
||||
});
|
||||
|
||||
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) {
|
||||
const token = getTokenAtPosition(sourceFile, pos);
|
||||
const decorator = findAncestor(token, isDecorator)!;
|
||||
Debug.assert(!!decorator, "Expected position to be owned by a decorator.");
|
||||
const replacement = factory.createParenthesizedExpression(decorator.expression);
|
||||
changeTracker.replaceNode(sourceFile, decorator.expression, replacement);
|
||||
}
|
||||
@@ -128,42 +128,43 @@ export class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
class CompilerTest {
|
||||
private static varyBy: readonly string[] = [
|
||||
"module",
|
||||
"moduleResolution",
|
||||
"moduleDetection",
|
||||
"allowArbitraryExtensions",
|
||||
"allowImportingTsExtensions",
|
||||
"target",
|
||||
"jsx",
|
||||
"noEmit",
|
||||
"removeComments",
|
||||
"importHelpers",
|
||||
"importHelpers",
|
||||
"downlevelIteration",
|
||||
"isolatedModules",
|
||||
"verbatimModuleSyntax",
|
||||
"strict",
|
||||
"noImplicitAny",
|
||||
"strictNullChecks",
|
||||
"strictFunctionTypes",
|
||||
"strictBindCallApply",
|
||||
"strictPropertyInitialization",
|
||||
"noImplicitThis",
|
||||
"alwaysStrict",
|
||||
"allowSyntheticDefaultImports",
|
||||
"esModuleInterop",
|
||||
"alwaysStrict",
|
||||
"downlevelIteration",
|
||||
"experimentalDecorators",
|
||||
"emitDecoratorMetadata",
|
||||
"skipDefaultLibCheck",
|
||||
"preserveConstEnums",
|
||||
"skipLibCheck",
|
||||
"esModuleInterop",
|
||||
"exactOptionalPropertyTypes",
|
||||
"useDefineForClassFields",
|
||||
"useUnknownInCatchVariables",
|
||||
"noUncheckedIndexedAccess",
|
||||
"importHelpers",
|
||||
"importHelpers",
|
||||
"isolatedModules",
|
||||
"jsx",
|
||||
"module",
|
||||
"moduleDetection",
|
||||
"moduleResolution",
|
||||
"noEmit",
|
||||
"noImplicitAny",
|
||||
"noImplicitThis",
|
||||
"noPropertyAccessFromIndexSignature",
|
||||
"noUncheckedIndexedAccess",
|
||||
"preserveConstEnums",
|
||||
"removeComments",
|
||||
"resolveJsonModule",
|
||||
"resolvePackageJsonExports",
|
||||
"resolvePackageJsonImports",
|
||||
"resolveJsonModule",
|
||||
"allowArbitraryExtensions",
|
||||
"skipDefaultLibCheck",
|
||||
"skipLibCheck",
|
||||
"strict",
|
||||
"strictBindCallApply",
|
||||
"strictFunctionTypes",
|
||||
"strictNullChecks",
|
||||
"strictPropertyInitialization",
|
||||
"target",
|
||||
"useDefineForClassFields",
|
||||
"useUnknownInCatchVariables",
|
||||
"verbatimModuleSyntax",
|
||||
];
|
||||
private fileName: string;
|
||||
private justName: string;
|
||||
|
||||
Reference in New Issue
Block a user