1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as ts from 'typescript'; 17 18function isInstanceofContext(tsIdentStart: ts.Node): boolean { 19 return ( 20 ts.isBinaryExpression(tsIdentStart.parent) && 21 tsIdentStart.parent.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword 22 ); 23} 24 25function isNewExpressionContext(tsIdentStart: ts.Node): boolean { 26 return ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression; 27} 28 29/* 30 * If identifier is the right-most name of Property Access chain or Qualified name, 31 * or it's a separate identifier expression, then identifier is being referenced as an value. 32 */ 33function isQualifiedNameContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { 34 // rightmost in AST is rightmost in qualified name chain 35 return ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right; 36} 37 38function isPropertyAccessContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { 39 // rightmost in AST is rightmost in qualified name chain 40 return ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name; 41} 42 43function getQualifiedStart(ident: ts.Node): ts.Node { 44 let qualifiedStart: ts.Node = ident; 45 while (ts.isPropertyAccessExpression(qualifiedStart.parent) || ts.isQualifiedName(qualifiedStart.parent)) { 46 qualifiedStart = qualifiedStart.parent; 47 } 48 return qualifiedStart; 49} 50 51function isEnumPropAccess(ident: ts.Identifier, tsSym: ts.Symbol, context: ts.Node): boolean { 52 return ( 53 ts.isElementAccessExpression(context) && 54 !!(tsSym.flags & ts.SymbolFlags.Enum) && 55 (context.expression === ident || 56 ts.isPropertyAccessExpression(context.expression) && context.expression.name === ident) 57 ); 58} 59 60function isValidParent(parent: ts.Node): boolean { 61 // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) 62 return ( 63 ts.isTypeNode(parent) && !ts.isTypeOfExpression(parent) || 64 ts.isExpressionWithTypeArguments(parent) || 65 ts.isExportAssignment(parent) || 66 ts.isExportSpecifier(parent) || 67 ts.isMetaProperty(parent) || 68 ts.isImportClause(parent) || 69 ts.isClassLike(parent) || 70 ts.isInterfaceDeclaration(parent) || 71 ts.isModuleDeclaration(parent) || 72 ts.isEnumDeclaration(parent) || 73 ts.isNamespaceImport(parent) || 74 ts.isImportSpecifier(parent) || 75 ts.isImportEqualsDeclaration(parent) 76 ); 77} 78 79export function identiferUseInValueContext(ident: ts.Identifier, tsSym: ts.Symbol): boolean { 80 const qualifiedStart = getQualifiedStart(ident); 81 const parent = qualifiedStart.parent; 82 const isValidUse = 83 isValidParent(parent) || 84 isEnumPropAccess(ident, tsSym, parent) || 85 isQualifiedNameContext(qualifiedStart, ident) || 86 isPropertyAccessContext(qualifiedStart, ident) || 87 isNewExpressionContext(qualifiedStart) || 88 isInstanceofContext(qualifiedStart); 89 return !isValidUse; 90} 91