1/* 2 * Copyright (c) 2022-2023 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 ts.isBinaryExpression(tsIdentStart.parent) && 20 tsIdentStart.parent.operatorToken.kind === ts.SyntaxKind.InstanceOfKeyword 21} 22 23function isNewExpressionContext(tsIdentStart: ts.Node): boolean { 24 return ts.isNewExpression(tsIdentStart.parent) && tsIdentStart === tsIdentStart.parent.expression 25} 26 27function isQualifiedNameContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { 28 // rightmost in AST is rightmost in qualified name chain 29 return ts.isQualifiedName(tsIdentStart) && tsIdentifier !== tsIdentStart.right 30} 31 32function isPropertyAccessContext(tsIdentStart: ts.Node, tsIdentifier: ts.Identifier): boolean { 33 // rightmost in AST is rightmost in qualified name chain 34 return ts.isPropertyAccessExpression(tsIdentStart) && tsIdentifier !== tsIdentStart.name 35} 36 37function getQualifiedStart(ident: ts.Node): ts.Node { 38 let qualifiedStart: ts.Node = ident; 39 while (ts.isPropertyAccessExpression(qualifiedStart.parent) || ts.isQualifiedName(qualifiedStart.parent)) { 40 qualifiedStart = qualifiedStart.parent; 41 } 42 return qualifiedStart; 43} 44 45function isEnumPropAccess(ident: ts.Identifier, tsSym: ts.Symbol, context: ts.Node): boolean { 46 return ts.isElementAccessExpression(context) && 47 (context as ts.ElementAccessExpression).expression == ident && 48 !!(tsSym.flags & ts.SymbolFlags.Enum); 49} 50 51function isValidTypeNode(node: ts.TypeNode): boolean { 52 return !ts.isTypeOfExpression(node); 53} 54 55export function identiferUseInValueContext( 56 ident: ts.Identifier, tsSym: ts.Symbol 57) { 58 let qualifiedStart = getQualifiedStart(ident); 59 let parent = qualifiedStart.parent; 60 61 return !( 62 // treat TypeQuery as valid because it's already forbidden (FaultID.TypeQuery) 63 ts.isTypeNode(parent) && isValidTypeNode(parent) || 64 // If identifier is the right-most name of Property Access chain or Qualified name, 65 // or it's a separate identifier expression, then identifier is being referenced as an value. 66 isEnumPropAccess(ident, tsSym, parent) || 67 ts.isExpressionWithTypeArguments(parent) || 68 ts.isExportAssignment(parent) || 69 ts.isExportSpecifier(parent) || 70 ts.isMetaProperty(parent) || 71 ts.isImportClause(parent) || 72 ts.isClassLike(parent) || 73 ts.isInterfaceDeclaration(parent) || 74 ts.isModuleDeclaration(parent) || 75 ts.isEnumDeclaration(parent) || 76 ts.isNamespaceImport(parent) || 77 ts.isImportSpecifier(parent) || 78 isQualifiedNameContext(qualifiedStart, ident) || 79 isPropertyAccessContext(qualifiedStart, ident) || 80 isNewExpressionContext(qualifiedStart) || 81 isInstanceofContext(qualifiedStart) || 82 ts.isImportEqualsDeclaration(parent) 83 ); 84} 85