ts2famix 1.4.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +30 -61
- package/dist/analyze.js +4 -2
- package/dist/analyze_functions/process_functions.js +285 -131
- package/dist/famix_functions/EntityDictionary.js +864 -231
- package/dist/famix_functions/helpers_creation.js +61 -10
- package/dist/fqn.js +160 -111
- package/dist/lib/famix/famix_JSON_exporter.js +55 -0
- package/dist/lib/famix/famix_base_element.js +18 -0
- package/dist/lib/famix/famix_repository.js +224 -0
- package/dist/lib/famix/{src/index.js → index.js} +1 -0
- package/dist/lib/famix/model/famix/access.js +40 -0
- package/dist/lib/famix/model/famix/accessor.js +17 -0
- package/dist/lib/famix/model/famix/alias.js +33 -0
- package/dist/lib/famix/model/famix/arrow_function.js +17 -0
- package/dist/lib/famix/model/famix/behavioral_entity.js +79 -0
- package/dist/lib/famix/model/famix/class.js +71 -0
- package/dist/lib/famix/model/famix/comment.js +39 -0
- package/dist/lib/famix/model/famix/concretisation.js +31 -0
- package/dist/lib/famix/model/famix/container_entity.js +126 -0
- package/dist/lib/famix/model/famix/decorator.js +32 -0
- package/dist/lib/famix/model/famix/entity.js +17 -0
- package/dist/lib/famix/model/famix/enum.js +31 -0
- package/dist/lib/famix/model/famix/enum_value.js +25 -0
- package/dist/lib/famix/model/famix/function.js +17 -0
- package/dist/lib/famix/model/famix/import_clause.js +41 -0
- package/dist/lib/famix/model/famix/index.js +86 -0
- package/dist/lib/famix/model/famix/indexed_file_anchor.js +38 -0
- package/dist/lib/famix/model/famix/inheritance.js +33 -0
- package/dist/lib/famix/model/famix/interface.js +64 -0
- package/dist/lib/famix/model/famix/invocation.js +54 -0
- package/dist/lib/famix/model/famix/method.js +67 -0
- package/dist/lib/famix/model/famix/module.js +60 -0
- package/dist/lib/famix/model/famix/named_entity.js +78 -0
- package/dist/lib/famix/model/famix/parameter.js +25 -0
- package/dist/lib/famix/model/famix/parameter_concretisation.js +44 -0
- package/dist/lib/famix/model/famix/parameter_type.js +45 -0
- package/dist/lib/famix/model/famix/parametric_arrow_function.js +31 -0
- package/dist/lib/famix/model/famix/parametric_class.js +44 -0
- package/dist/lib/famix/model/famix/parametric_function.js +31 -0
- package/dist/lib/famix/model/famix/parametric_interface.js +44 -0
- package/dist/lib/famix/model/famix/parametric_method.js +31 -0
- package/dist/lib/famix/model/famix/primitive_type.js +17 -0
- package/dist/lib/famix/model/famix/property.js +73 -0
- package/dist/lib/famix/model/famix/reference.js +33 -0
- package/dist/lib/famix/model/famix/scoping_entity.js +36 -0
- package/dist/lib/famix/model/famix/script_entity.js +29 -0
- package/dist/lib/famix/model/famix/source_anchor.js +27 -0
- package/dist/lib/famix/model/famix/source_language.js +35 -0
- package/dist/lib/famix/model/famix/sourced_entity.js +60 -0
- package/dist/lib/famix/model/famix/structural_entity.js +39 -0
- package/dist/lib/famix/model/famix/type.js +73 -0
- package/dist/lib/famix/model/famix/variable.js +24 -0
- package/dist/lib/ts-complex/cyclomatic-service.js +3 -3
- package/dist/refactorer/refactor-getter-setter.js +142 -0
- package/dist/ts2famix-cli-wrapper.js +42 -0
- package/dist/ts2famix-cli.js +8 -1
- package/dist/ts2famix-tsconfig.js +1 -0
- package/doc-uml/famix-typescript-model.puml +608 -0
- package/doc-uml/famix-typescript-model.svg +1 -0
- package/jest.config.json +2 -1
- package/package.json +13 -12
- package/src/analyze.ts +24 -23
- package/src/analyze_functions/process_functions.ts +310 -129
- package/src/famix_functions/EntityDictionary.ts +949 -271
- package/src/famix_functions/helpers_creation.ts +64 -6
- package/src/fqn.ts +169 -96
- package/{dist/lib/famix/src/famix_JSON_exporter.js → src/lib/famix/famix_JSON_exporter.ts} +16 -14
- package/src/lib/famix/famix_base_element.ts +22 -0
- package/{dist/lib/famix/src/famix_repository.js → src/lib/famix/famix_repository.ts} +96 -75
- package/src/lib/famix/model/famix/access.ts +50 -0
- package/src/lib/famix/model/famix/alias.ts +39 -0
- package/src/lib/famix/{src/model/famix/implicit_variable.ts → model/famix/arrow_function.ts} +3 -3
- package/src/lib/famix/model/famix/behavioral_entity.ts +97 -0
- package/src/lib/famix/model/famix/class.ts +85 -0
- package/src/lib/famix/model/famix/comment.ts +47 -0
- package/src/lib/famix/model/famix/concretisation.ts +40 -0
- package/src/lib/famix/model/famix/container_entity.ts +160 -0
- package/src/lib/famix/model/famix/decorator.ts +37 -0
- package/src/lib/famix/model/famix/enum.ts +30 -0
- package/src/lib/famix/model/famix/enum_value.ts +28 -0
- package/src/lib/famix/model/famix/import_clause.ts +51 -0
- package/src/lib/famix/{src/model → model}/famix/index.ts +8 -7
- package/src/lib/famix/model/famix/indexed_file_anchor.ts +46 -0
- package/src/lib/famix/model/famix/inheritance.ts +40 -0
- package/src/lib/famix/model/famix/interface.ts +75 -0
- package/src/lib/famix/model/famix/invocation.ts +65 -0
- package/src/lib/famix/model/famix/method.ts +89 -0
- package/src/lib/famix/model/famix/module.ts +71 -0
- package/src/lib/famix/model/famix/named_entity.ts +95 -0
- package/src/lib/famix/{src/model → model}/famix/parameter.ts +11 -12
- package/src/lib/famix/model/famix/parameter_concretisation.ts +51 -0
- package/src/lib/famix/model/famix/parameter_type.ts +58 -0
- package/src/lib/famix/model/famix/parametric_arrow_function.ts +32 -0
- package/src/lib/famix/model/famix/parametric_class.ts +49 -0
- package/src/lib/famix/model/famix/parametric_function.ts +32 -0
- package/src/lib/famix/model/famix/parametric_interface.ts +49 -0
- package/src/lib/famix/model/famix/parametric_method.ts +32 -0
- package/src/lib/famix/model/famix/primitive_type.ts +15 -0
- package/src/lib/famix/model/famix/property.ts +94 -0
- package/src/lib/famix/model/famix/reference.ts +40 -0
- package/src/lib/famix/model/famix/scoping_entity.ts +35 -0
- package/src/lib/famix/model/famix/script_entity.ts +34 -0
- package/src/lib/famix/model/famix/source_anchor.ts +30 -0
- package/src/lib/famix/model/famix/source_language.ts +35 -0
- package/src/lib/famix/model/famix/sourced_entity.ts +70 -0
- package/src/lib/famix/model/famix/structural_entity.ts +43 -0
- package/src/lib/famix/model/famix/type.ts +87 -0
- package/src/lib/famix/model/famix/variable.ts +27 -0
- package/src/lib/famix/package.json +1 -1
- package/src/lib/ts-complex/cyclomatic-service.ts +10 -10
- package/src/refactorer/refactor-getter-setter.ts +140 -0
- package/src/ts2famix-cli-wrapper.ts +21 -0
- package/src/ts2famix-cli.ts +8 -2
- package/tsconfig.check-tests.json +14 -0
- package/tsconfig.json +71 -69
- package/dist/famix2puml.js +0 -125
- package/dist/lib/famix/src/famix_base_element.js +0 -17
- package/dist/lib/famix/src/model/famix/access.js +0 -39
- package/dist/lib/famix/src/model/famix/accessor.js +0 -16
- package/dist/lib/famix/src/model/famix/alias.js +0 -32
- package/dist/lib/famix/src/model/famix/association.js +0 -36
- package/dist/lib/famix/src/model/famix/behavioral_entity.js +0 -81
- package/dist/lib/famix/src/model/famix/class.js +0 -70
- package/dist/lib/famix/src/model/famix/comment.js +0 -38
- package/dist/lib/famix/src/model/famix/container_entity.js +0 -125
- package/dist/lib/famix/src/model/famix/decorator.js +0 -31
- package/dist/lib/famix/src/model/famix/entity.js +0 -16
- package/dist/lib/famix/src/model/famix/enum.js +0 -30
- package/dist/lib/famix/src/model/famix/enum_value.js +0 -24
- package/dist/lib/famix/src/model/famix/function.js +0 -16
- package/dist/lib/famix/src/model/famix/implicit_variable.js +0 -16
- package/dist/lib/famix/src/model/famix/import_clause.js +0 -39
- package/dist/lib/famix/src/model/famix/index.js +0 -83
- package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +0 -51
- package/dist/lib/famix/src/model/famix/inheritance.js +0 -32
- package/dist/lib/famix/src/model/famix/interface.js +0 -63
- package/dist/lib/famix/src/model/famix/invocation.js +0 -53
- package/dist/lib/famix/src/model/famix/method.js +0 -66
- package/dist/lib/famix/src/model/famix/module.js +0 -31
- package/dist/lib/famix/src/model/famix/named_entity.js +0 -77
- package/dist/lib/famix/src/model/famix/namespace.js +0 -24
- package/dist/lib/famix/src/model/famix/parameter.js +0 -24
- package/dist/lib/famix/src/model/famix/parameter_type.js +0 -24
- package/dist/lib/famix/src/model/famix/parameterizable_class.js +0 -30
- package/dist/lib/famix/src/model/famix/parameterizable_interface.js +0 -30
- package/dist/lib/famix/src/model/famix/parameterized_type.js +0 -36
- package/dist/lib/famix/src/model/famix/primitive_type.js +0 -16
- package/dist/lib/famix/src/model/famix/property.js +0 -44
- package/dist/lib/famix/src/model/famix/reference.js +0 -32
- package/dist/lib/famix/src/model/famix/scoping_entity.js +0 -35
- package/dist/lib/famix/src/model/famix/script_entity.js +0 -30
- package/dist/lib/famix/src/model/famix/source_anchor.js +0 -26
- package/dist/lib/famix/src/model/famix/source_language.js +0 -35
- package/dist/lib/famix/src/model/famix/sourced_entity.js +0 -59
- package/dist/lib/famix/src/model/famix/structural_entity.js +0 -38
- package/dist/lib/famix/src/model/famix/text_anchor.js +0 -37
- package/dist/lib/famix/src/model/famix/type.js +0 -71
- package/dist/lib/famix/src/model/famix/variable.js +0 -23
- package/doc-uml/metamodel-full.svg +0 -1
- package/doc-uml/metamodel.svg +0 -1
- package/jest.config-old.ts +0 -199
- package/plantuml.jar +0 -0
- package/src/famix2puml.ts +0 -119
- package/src/lib/famix/package-lock.json +0 -301
- package/src/lib/famix/readme.md +0 -5
- package/src/lib/famix/src/famix_JSON_exporter.ts +0 -56
- package/src/lib/famix/src/famix_base_element.ts +0 -22
- package/src/lib/famix/src/famix_repository.ts +0 -243
- package/src/lib/famix/src/model/famix/access.ts +0 -53
- package/src/lib/famix/src/model/famix/alias.ts +0 -41
- package/src/lib/famix/src/model/famix/association.ts +0 -44
- package/src/lib/famix/src/model/famix/behavioral_entity.ts +0 -107
- package/src/lib/famix/src/model/famix/class.ts +0 -86
- package/src/lib/famix/src/model/famix/comment.ts +0 -50
- package/src/lib/famix/src/model/famix/container_entity.ts +0 -165
- package/src/lib/famix/src/model/famix/decorator.ts +0 -39
- package/src/lib/famix/src/model/famix/enum.ts +0 -31
- package/src/lib/famix/src/model/famix/enum_value.ts +0 -29
- package/src/lib/famix/src/model/famix/import_clause.ts +0 -53
- package/src/lib/famix/src/model/famix/indexed_file_anchor.ts +0 -71
- package/src/lib/famix/src/model/famix/inheritance.ts +0 -42
- package/src/lib/famix/src/model/famix/interface.ts +0 -75
- package/src/lib/famix/src/model/famix/invocation.ts +0 -68
- package/src/lib/famix/src/model/famix/method.ts +0 -96
- package/src/lib/famix/src/model/famix/module.ts +0 -31
- package/src/lib/famix/src/model/famix/named_entity.ts +0 -98
- package/src/lib/famix/src/model/famix/namespace.ts +0 -28
- package/src/lib/famix/src/model/famix/parameter_type.ts +0 -33
- package/src/lib/famix/src/model/famix/parameterizable_class.ts +0 -31
- package/src/lib/famix/src/model/famix/parameterizable_interface.ts +0 -31
- package/src/lib/famix/src/model/famix/parameterized_type.ts +0 -40
- package/src/lib/famix/src/model/famix/primitive_type.ts +0 -15
- package/src/lib/famix/src/model/famix/property.ts +0 -54
- package/src/lib/famix/src/model/famix/reference.ts +0 -42
- package/src/lib/famix/src/model/famix/scoping_entity.ts +0 -35
- package/src/lib/famix/src/model/famix/script_entity.ts +0 -38
- package/src/lib/famix/src/model/famix/source_anchor.ts +0 -31
- package/src/lib/famix/src/model/famix/source_language.ts +0 -37
- package/src/lib/famix/src/model/famix/sourced_entity.ts +0 -73
- package/src/lib/famix/src/model/famix/structural_entity.ts +0 -44
- package/src/lib/famix/src/model/famix/text_anchor.ts +0 -49
- package/src/lib/famix/src/model/famix/type.ts +0 -88
- package/src/lib/famix/src/model/famix/variable.ts +0 -28
- package/src/lib/famix/tsconfig.json +0 -27
- package/src/lib/famix/tslint.json +0 -15
- /package/src/lib/famix/{src/index.ts → index.ts} +0 -0
- /package/src/lib/famix/{src/model → model}/famix/accessor.ts +0 -0
- /package/src/lib/famix/{src/model → model}/famix/entity.ts +0 -0
- /package/src/lib/famix/{src/model → model}/famix/function.ts +0 -0
|
@@ -1,29 +1,94 @@
|
|
|
1
|
-
import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression, ExpressionWithTypeArguments, ImportDeclaration, ImportEqualsDeclaration } from "ts-morph";
|
|
2
|
-
import
|
|
1
|
+
import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression, ExpressionWithTypeArguments, ImportDeclaration, ImportEqualsDeclaration, SyntaxKind, Expression, TypeNode, Node, ts, Scope, Type, ArrowFunction } from "ts-morph";
|
|
2
|
+
import { isAmbient, isNamespace } from "../analyze_functions/process_functions";
|
|
3
|
+
import * as Famix from "../lib/famix/model/famix";
|
|
4
|
+
import { FamixRepository } from "../lib/famix/famix_repository";
|
|
3
5
|
import { logger, config } from "../analyze";
|
|
4
6
|
import GraphemeSplitter from "grapheme-splitter";
|
|
5
7
|
import * as Helpers from "./helpers_creation";
|
|
6
8
|
import * as FQNFunctions from "../fqn";
|
|
7
|
-
import { FamixRepository } from "../lib/famix/src/famix_repository";
|
|
8
9
|
import path from "path";
|
|
10
|
+
import _ from 'lodash';
|
|
9
11
|
|
|
10
12
|
export type TSMorphObjectType = ImportDeclaration | ImportEqualsDeclaration | SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | TypeParameterDeclaration | Identifier | Decorator | GetAccessorDeclaration | SetAccessorDeclaration | ImportSpecifier | CommentRange | EnumDeclaration | EnumMember | TypeAliasDeclaration | ExpressionWithTypeArguments;
|
|
11
13
|
|
|
14
|
+
export type TypeDeclaration = TypeAliasDeclaration | PropertyDeclaration | PropertySignature | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | EnumMember | ImportEqualsDeclaration;
|
|
15
|
+
|
|
16
|
+
type ParametricVariantType = Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod;
|
|
17
|
+
|
|
18
|
+
type ConcreteElementTSMorphType = ClassDeclaration | InterfaceDeclaration | FunctionDeclaration | MethodDeclaration;
|
|
19
|
+
|
|
12
20
|
export class EntityDictionary {
|
|
13
21
|
|
|
14
22
|
public famixRep = new FamixRepository();
|
|
15
23
|
private fmxAliasMap = new Map<string, Famix.Alias>(); // Maps the alias names to their Famix model
|
|
16
|
-
private fmxClassMap = new Map<string, Famix.Class | Famix.
|
|
17
|
-
private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.
|
|
18
|
-
private
|
|
24
|
+
private fmxClassMap = new Map<string, Famix.Class | Famix.ParametricClass>(); // Maps the fully qualified class names to their Famix model
|
|
25
|
+
private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.ParametricInterface>(); // Maps the interface names to their Famix model
|
|
26
|
+
private fmxModuleMap = new Map<string, Famix.Module>(); // Maps the namespace names to their Famix model
|
|
19
27
|
private fmxFileMap = new Map<string, Famix.ScriptEntity | Famix.Module>(); // Maps the source file names to their Famix model
|
|
20
|
-
private fmxTypeMap = new Map<string, Famix.Type | Famix.PrimitiveType | Famix.
|
|
28
|
+
private fmxTypeMap = new Map<string, Famix.Type | Famix.PrimitiveType | Famix.ParameterType>(); // Maps the type names to their Famix model
|
|
29
|
+
private fmxFunctionAndMethodMap = new Map<string, Famix.Function | Famix.ParametricFunction | Famix.Method | Famix.ParametricMethod> // Maps the function names to their Famix model
|
|
21
30
|
private UNKNOWN_VALUE = '(unknown due to parsing error)'; // The value to use when a name is not usable
|
|
22
31
|
public fmxElementObjectMap = new Map<Famix.Entity,TSMorphObjectType>();
|
|
32
|
+
public tsMorphElementObjectMap = new Map<TSMorphObjectType,Famix.Entity>();
|
|
23
33
|
|
|
24
34
|
constructor() {
|
|
25
|
-
this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
|
|
35
|
+
this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public addSourceAnchor(fmx: Famix.SourcedEntity, node: TSMorphObjectType): Famix.IndexedFileAnchor {
|
|
39
|
+
const sourceAnchor: Famix.IndexedFileAnchor = new Famix.IndexedFileAnchor();
|
|
40
|
+
let sourceStart, sourceEnd: number;
|
|
41
|
+
if (fmx && node) {
|
|
42
|
+
// find the start and end positions of the source element
|
|
43
|
+
if (!(node instanceof CommentRange)) {
|
|
44
|
+
sourceStart = node.getStart();
|
|
45
|
+
sourceEnd = node.getEnd();
|
|
46
|
+
} else {
|
|
47
|
+
sourceStart = node.getPos();
|
|
48
|
+
sourceEnd = node.getEnd();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (config.expectGraphemes) {
|
|
52
|
+
/**
|
|
53
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
54
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
55
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
56
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
57
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
58
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
59
|
+
* Famix index file anchor.
|
|
60
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
61
|
+
*/
|
|
62
|
+
const splitter = new GraphemeSplitter();
|
|
63
|
+
const sourceFileText = node.getSourceFile().getFullText();
|
|
64
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
65
|
+
if (hasGraphemeClusters) {
|
|
66
|
+
const sourceElementText = sourceFileText.substring(sourceStart, sourceEnd);
|
|
67
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
68
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
69
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, sourceStart));
|
|
26
70
|
|
|
71
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
72
|
+
sourceStart = Helpers.indexOfSplitArray({searchArray: sourceFileTextGraphemes,
|
|
73
|
+
targetArray: sourceElementTextGraphemes,
|
|
74
|
+
start: sourceStart - numberOfGraphemeClustersBeforeStart});
|
|
75
|
+
sourceEnd = sourceStart + sourceElementTextGraphemes.length;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The +1 is because the source anchor (Pharo) is 1-based, but ts-morph is 0-based
|
|
80
|
+
sourceAnchor.startPos = sourceStart + 1;
|
|
81
|
+
sourceAnchor.endPos = sourceEnd + 1;
|
|
82
|
+
|
|
83
|
+
const fileName = node.getSourceFile().getFilePath();
|
|
84
|
+
|
|
85
|
+
sourceAnchor.element = fmx;
|
|
86
|
+
sourceAnchor.fileName = fileName;
|
|
87
|
+
fmx.sourceAnchor = sourceAnchor;
|
|
88
|
+
this.famixRep.addElement(sourceAnchor);
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
return sourceAnchor;
|
|
27
92
|
}
|
|
28
93
|
|
|
29
94
|
/**
|
|
@@ -32,10 +97,19 @@ export class EntityDictionary {
|
|
|
32
97
|
* @param famixElement The Famix model of the source element
|
|
33
98
|
*/
|
|
34
99
|
public makeFamixIndexFileAnchor(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity): void {
|
|
100
|
+
// check if famixElement doesn't have a valid fullyQualifiedName
|
|
101
|
+
if (typeof (famixElement as any).getFullyQualifiedName === 'function') {
|
|
102
|
+
// The method exists
|
|
103
|
+
const fullyQualifiedName = (famixElement as any).fullyQualifiedName;
|
|
104
|
+
if (!fullyQualifiedName || fullyQualifiedName === this.UNKNOWN_VALUE) {
|
|
105
|
+
throw new Error(`Famix element ${famixElement.constructor.name} has no valid fullyQualifiedName.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
35
109
|
logger.debug("making index file anchor for '" + sourceElement?.getText() + "' with famixElement " + famixElement.getJSON());
|
|
36
110
|
const fmxIndexFileAnchor = new Famix.IndexedFileAnchor();
|
|
37
|
-
fmxIndexFileAnchor.
|
|
38
|
-
this.fmxElementObjectMap.set(famixElement,sourceElement);
|
|
111
|
+
fmxIndexFileAnchor.element = famixElement;
|
|
112
|
+
this.fmxElementObjectMap.set(famixElement, sourceElement);
|
|
39
113
|
|
|
40
114
|
if (sourceElement !== null) {
|
|
41
115
|
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
@@ -56,7 +130,7 @@ export class EntityDictionary {
|
|
|
56
130
|
// revert any backslashes to forward slashes (path.normalize on windows introduces them)
|
|
57
131
|
pathInProject = pathInProject.replace(/\\/g, "/");
|
|
58
132
|
|
|
59
|
-
fmxIndexFileAnchor.
|
|
133
|
+
fmxIndexFileAnchor.fileName = pathInProject;
|
|
60
134
|
let sourceStart, sourceEnd, sourceLineStart, sourceLineEnd: number;
|
|
61
135
|
if (!(sourceElement instanceof CommentRange)) {
|
|
62
136
|
sourceStart = sourceElement.getStart();
|
|
@@ -95,22 +169,18 @@ export class EntityDictionary {
|
|
|
95
169
|
}
|
|
96
170
|
}
|
|
97
171
|
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
98
|
-
fmxIndexFileAnchor.
|
|
99
|
-
fmxIndexFileAnchor.
|
|
100
|
-
if (!(sourceElement instanceof CommentRange)) {
|
|
101
|
-
fmxIndexFileAnchor.setStartLine(sourceLineStart);
|
|
102
|
-
fmxIndexFileAnchor.setEndLine(sourceLineEnd);
|
|
103
|
-
}
|
|
172
|
+
fmxIndexFileAnchor.startPos = sourceStart + 1;
|
|
173
|
+
fmxIndexFileAnchor.endPos = sourceEnd + 1;
|
|
104
174
|
|
|
105
|
-
if (!(famixElement instanceof Famix.
|
|
106
|
-
|
|
107
|
-
}
|
|
175
|
+
// if (!(famixElement instanceof Famix.ImportClause || famixElement instanceof Famix.Access || famixElement instanceof Famix.Reference || famixElement instanceof Famix.Invocation || famixElement instanceof Famix.Inheritance) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof CommentRange) && !(sourceElement instanceof Identifier) && !(sourceElement instanceof ImportSpecifier) && !(sourceElement instanceof ExpressionWithTypeArguments)) {
|
|
176
|
+
// initFQN(sourceElement, famixElement);
|
|
177
|
+
// }
|
|
108
178
|
} else {
|
|
109
179
|
// sourceElement is null
|
|
110
180
|
logger.warn("sourceElement is null for famixElement " + famixElement.getJSON());
|
|
111
|
-
fmxIndexFileAnchor.
|
|
112
|
-
fmxIndexFileAnchor.
|
|
113
|
-
fmxIndexFileAnchor.
|
|
181
|
+
fmxIndexFileAnchor.fileName = "unknown";
|
|
182
|
+
fmxIndexFileAnchor.startPos = 0;
|
|
183
|
+
fmxIndexFileAnchor.endPos = 0;
|
|
114
184
|
}
|
|
115
185
|
|
|
116
186
|
this.famixRep.addElement(fmxIndexFileAnchor);
|
|
@@ -123,20 +193,23 @@ export class EntityDictionary {
|
|
|
123
193
|
* @returns The Famix model of the source file
|
|
124
194
|
*/
|
|
125
195
|
public createOrGetFamixFile(f: SourceFile, isModule: boolean): Famix.ScriptEntity | Famix.Module {
|
|
126
|
-
let fmxFile: Famix.ScriptEntity | Famix.Module;
|
|
196
|
+
let fmxFile: Famix.ScriptEntity; // | Famix.Module;
|
|
127
197
|
|
|
128
198
|
const fileName = f.getBaseName();
|
|
129
199
|
const fullyQualifiedFilename = f.getFilePath();
|
|
130
|
-
|
|
200
|
+
const foundFileName = this.fmxFileMap.get(fullyQualifiedFilename);
|
|
201
|
+
if (!foundFileName) {
|
|
131
202
|
if (isModule) {
|
|
132
|
-
fmxFile = new Famix.Module();
|
|
203
|
+
fmxFile = new Famix.Module();
|
|
133
204
|
}
|
|
134
205
|
else {
|
|
135
206
|
fmxFile = new Famix.ScriptEntity();
|
|
136
207
|
}
|
|
137
|
-
fmxFile.
|
|
138
|
-
fmxFile.
|
|
139
|
-
fmxFile.
|
|
208
|
+
fmxFile.name = fileName;
|
|
209
|
+
fmxFile.numberOfLinesOfText = f.getEndLineNumber() - f.getStartLineNumber();
|
|
210
|
+
fmxFile.numberOfCharacters = f.getFullText().length;
|
|
211
|
+
|
|
212
|
+
initFQN(f, fmxFile);
|
|
140
213
|
|
|
141
214
|
this.makeFamixIndexFileAnchor(f, fmxFile);
|
|
142
215
|
|
|
@@ -144,7 +217,7 @@ export class EntityDictionary {
|
|
|
144
217
|
this.famixRep.addElement(fmxFile);
|
|
145
218
|
}
|
|
146
219
|
else {
|
|
147
|
-
fmxFile =
|
|
220
|
+
fmxFile = foundFileName;
|
|
148
221
|
}
|
|
149
222
|
|
|
150
223
|
this.fmxElementObjectMap.set(fmxFile,f);
|
|
@@ -152,29 +225,34 @@ export class EntityDictionary {
|
|
|
152
225
|
}
|
|
153
226
|
|
|
154
227
|
/**
|
|
155
|
-
* Creates or gets a Famix
|
|
156
|
-
* @param m A
|
|
157
|
-
* @returns The Famix model of the
|
|
228
|
+
* Creates or gets a Famix Module
|
|
229
|
+
* @param m A module
|
|
230
|
+
* @returns The Famix model of the module
|
|
158
231
|
*/
|
|
159
|
-
public
|
|
160
|
-
let
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
232
|
+
public createOrGetFamixModule(m: ModuleDeclaration): Famix.Module {
|
|
233
|
+
let fmxModule: Famix.Module;
|
|
234
|
+
const moduleName = m.getName();
|
|
235
|
+
const foundModuleName = this.fmxModuleMap.get(moduleName);
|
|
236
|
+
if (!foundModuleName) {
|
|
237
|
+
fmxModule = new Famix.Module();
|
|
238
|
+
fmxModule.name = moduleName;
|
|
239
|
+
fmxModule.isAmbient = isAmbient(m);
|
|
240
|
+
fmxModule.isNamespace = isNamespace(m);
|
|
241
|
+
fmxModule.isModule = !fmxModule.isNamespace && !fmxModule.isAmbient;
|
|
242
|
+
|
|
243
|
+
initFQN(m, fmxModule);
|
|
244
|
+
this.makeFamixIndexFileAnchor(m, fmxModule);
|
|
245
|
+
|
|
246
|
+
this.fmxModuleMap.set(moduleName, fmxModule);
|
|
247
|
+
|
|
248
|
+
this.famixRep.addElement(fmxModule);
|
|
171
249
|
}
|
|
172
250
|
else {
|
|
173
|
-
|
|
251
|
+
fmxModule = foundModuleName;
|
|
174
252
|
}
|
|
175
253
|
|
|
176
|
-
this.fmxElementObjectMap.set(
|
|
177
|
-
return
|
|
254
|
+
this.fmxElementObjectMap.set(fmxModule,m);
|
|
255
|
+
return fmxModule;
|
|
178
256
|
}
|
|
179
257
|
|
|
180
258
|
/**
|
|
@@ -186,15 +264,16 @@ export class EntityDictionary {
|
|
|
186
264
|
let fmxAlias: Famix.Alias;
|
|
187
265
|
const aliasName = a.getName();
|
|
188
266
|
const aliasFullyQualifiedName = a.getType().getText(); // FQNFunctions.getFQN(a);
|
|
189
|
-
|
|
267
|
+
const foundAlias = this.fmxAliasMap.get(aliasFullyQualifiedName);
|
|
268
|
+
if (!foundAlias) {
|
|
190
269
|
fmxAlias = new Famix.Alias();
|
|
191
|
-
fmxAlias.
|
|
270
|
+
fmxAlias.name = a.getName();
|
|
192
271
|
const aliasNameWithGenerics = aliasName + (a.getTypeParameters().length ? ("<" + a.getTypeParameters().map(tp => tp.getName()).join(", ") + ">") : "");
|
|
193
272
|
logger.debug(`> NOTE: alias ${aliasName} has fully qualified name ${aliasFullyQualifiedName} and name with generics ${aliasNameWithGenerics}.`);
|
|
194
273
|
|
|
195
274
|
const fmxType = this.createOrGetFamixType(aliasNameWithGenerics, a);
|
|
196
|
-
fmxAlias.
|
|
197
|
-
|
|
275
|
+
fmxAlias.aliasedEntity = fmxType;
|
|
276
|
+
initFQN(a, fmxAlias);
|
|
198
277
|
this.makeFamixIndexFileAnchor(a, fmxAlias);
|
|
199
278
|
|
|
200
279
|
this.fmxAliasMap.set(aliasFullyQualifiedName, fmxAlias);
|
|
@@ -202,7 +281,7 @@ export class EntityDictionary {
|
|
|
202
281
|
this.famixRep.addElement(fmxAlias);
|
|
203
282
|
}
|
|
204
283
|
else {
|
|
205
|
-
fmxAlias =
|
|
284
|
+
fmxAlias = foundAlias;
|
|
206
285
|
}
|
|
207
286
|
this.fmxElementObjectMap.set(fmxAlias,a);
|
|
208
287
|
|
|
@@ -214,23 +293,24 @@ export class EntityDictionary {
|
|
|
214
293
|
* @param cls A class
|
|
215
294
|
* @returns The Famix model of the class
|
|
216
295
|
*/
|
|
217
|
-
public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.
|
|
218
|
-
let fmxClass: Famix.Class | Famix.
|
|
296
|
+
public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParametricClass {
|
|
297
|
+
let fmxClass: Famix.Class | Famix.ParametricClass;
|
|
219
298
|
const isAbstract = cls.isAbstract();
|
|
220
299
|
const classFullyQualifiedName = FQNFunctions.getFQN(cls);
|
|
221
|
-
const clsName = cls.getName();
|
|
222
|
-
|
|
223
|
-
|
|
300
|
+
const clsName = cls.getName() || this.UNKNOWN_VALUE;
|
|
301
|
+
const isGeneric = cls.getTypeParameters().length;
|
|
302
|
+
const foundClass = this.fmxClassMap.get(classFullyQualifiedName);
|
|
303
|
+
if (!foundClass) {
|
|
224
304
|
if (isGeneric) {
|
|
225
|
-
fmxClass = new Famix.
|
|
305
|
+
fmxClass = new Famix.ParametricClass();
|
|
226
306
|
}
|
|
227
307
|
else {
|
|
228
308
|
fmxClass = new Famix.Class();
|
|
229
309
|
}
|
|
230
310
|
|
|
231
|
-
fmxClass.
|
|
232
|
-
fmxClass.
|
|
233
|
-
fmxClass.
|
|
311
|
+
fmxClass.name = clsName;
|
|
312
|
+
fmxClass.fullyQualifiedName = classFullyQualifiedName;
|
|
313
|
+
fmxClass.isAbstract = isAbstract;
|
|
234
314
|
|
|
235
315
|
this.makeFamixIndexFileAnchor(cls, fmxClass);
|
|
236
316
|
|
|
@@ -238,13 +318,12 @@ export class EntityDictionary {
|
|
|
238
318
|
|
|
239
319
|
this.famixRep.addElement(fmxClass);
|
|
240
320
|
|
|
321
|
+
this.fmxElementObjectMap.set(fmxClass,cls);
|
|
241
322
|
}
|
|
242
323
|
else {
|
|
243
|
-
fmxClass =
|
|
324
|
+
fmxClass = foundClass;
|
|
244
325
|
}
|
|
245
326
|
|
|
246
|
-
this.fmxElementObjectMap.set(fmxClass,cls);
|
|
247
|
-
|
|
248
327
|
return fmxClass;
|
|
249
328
|
}
|
|
250
329
|
|
|
@@ -253,35 +332,98 @@ export class EntityDictionary {
|
|
|
253
332
|
* @param inter An interface
|
|
254
333
|
* @returns The Famix model of the interface
|
|
255
334
|
*/
|
|
256
|
-
public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.
|
|
257
|
-
|
|
335
|
+
public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
|
|
336
|
+
|
|
337
|
+
let fmxInterface: Famix.Interface | Famix.ParametricInterface;
|
|
258
338
|
const interName = inter.getName();
|
|
259
339
|
const interFullyQualifiedName = FQNFunctions.getFQN(inter);
|
|
260
|
-
|
|
340
|
+
const foundInterface = this.fmxInterfaceMap.get(interFullyQualifiedName);
|
|
341
|
+
if (!foundInterface) {
|
|
261
342
|
const isGeneric = inter.getTypeParameters().length;
|
|
262
343
|
if (isGeneric) {
|
|
263
|
-
fmxInterface = new Famix.
|
|
344
|
+
fmxInterface = new Famix.ParametricInterface();
|
|
264
345
|
}
|
|
265
346
|
else {
|
|
266
347
|
fmxInterface = new Famix.Interface();
|
|
267
348
|
}
|
|
268
349
|
|
|
269
|
-
fmxInterface.
|
|
270
|
-
|
|
350
|
+
fmxInterface.name = interName;
|
|
351
|
+
initFQN(inter, fmxInterface);
|
|
271
352
|
this.makeFamixIndexFileAnchor(inter, fmxInterface);
|
|
272
353
|
|
|
273
354
|
this.fmxInterfaceMap.set(interFullyQualifiedName, fmxInterface);
|
|
274
355
|
|
|
275
356
|
this.famixRep.addElement(fmxInterface);
|
|
357
|
+
|
|
358
|
+
this.fmxElementObjectMap.set(fmxInterface,inter);
|
|
276
359
|
}
|
|
277
360
|
else {
|
|
278
|
-
fmxInterface =
|
|
361
|
+
fmxInterface = foundInterface;
|
|
279
362
|
}
|
|
280
|
-
|
|
281
|
-
this.fmxElementObjectMap.set(fmxInterface,inter);
|
|
282
363
|
return fmxInterface;
|
|
283
364
|
}
|
|
284
365
|
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Creates or gets a Famix concrete element
|
|
369
|
+
* @param concreteElement A parametric Element
|
|
370
|
+
* @param concreteElementDeclaration the element declaration
|
|
371
|
+
* @param concreteArguments concrete arguments
|
|
372
|
+
* @returns A parametric Element
|
|
373
|
+
*/
|
|
374
|
+
public createOrGetFamixConcreteElement(concreteElement : ParametricVariantType,
|
|
375
|
+
concreteElementDeclaration : ConcreteElementTSMorphType,
|
|
376
|
+
concreteArguments: TypeNode[]): ParametricVariantType {
|
|
377
|
+
|
|
378
|
+
let fullyQualifiedFilename = concreteElement.fullyQualifiedName;
|
|
379
|
+
let params = "";
|
|
380
|
+
|
|
381
|
+
concreteArguments.map((param) => {
|
|
382
|
+
params = params+param.getText()+','
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
params = params.substring(0, params.length - 1)
|
|
386
|
+
|
|
387
|
+
fullyQualifiedFilename = Helpers.replaceLastBetweenTags(fullyQualifiedFilename,params);
|
|
388
|
+
|
|
389
|
+
let concElement: ParametricVariantType;
|
|
390
|
+
|
|
391
|
+
if (!this.fmxInterfaceMap.has(fullyQualifiedFilename) &&
|
|
392
|
+
!this.fmxClassMap.has(fullyQualifiedFilename) &&
|
|
393
|
+
!this.fmxFunctionAndMethodMap.has(fullyQualifiedFilename)){
|
|
394
|
+
concElement = _.cloneDeep(concreteElement);
|
|
395
|
+
concElement.fullyQualifiedName = fullyQualifiedFilename;
|
|
396
|
+
concElement.clearGenericParameters();
|
|
397
|
+
concreteArguments.map((param) => {
|
|
398
|
+
const parameter = this.createOrGetFamixConcreteType(param);
|
|
399
|
+
concElement.addConcreteParameter(parameter);
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
if (concreteElement instanceof Famix.ParametricClass) {
|
|
403
|
+
this.fmxClassMap.set(fullyQualifiedFilename, concElement as Famix.ParametricClass);
|
|
404
|
+
} else if (concreteElement instanceof Famix.ParametricInterface) {
|
|
405
|
+
this.fmxInterfaceMap.set(fullyQualifiedFilename, concElement as Famix.ParametricInterface);
|
|
406
|
+
} else if (concreteElement instanceof Famix.ParametricFunction) {
|
|
407
|
+
this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricFunction);
|
|
408
|
+
} else { // if (concreteElement instanceof Famix.ParametricMethod) {
|
|
409
|
+
this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricMethod);
|
|
410
|
+
}
|
|
411
|
+
this.famixRep.addElement(concElement);
|
|
412
|
+
this.fmxElementObjectMap.set(concElement,concreteElementDeclaration);
|
|
413
|
+
} else {
|
|
414
|
+
if (concreteElement instanceof Famix.ParametricClass) {
|
|
415
|
+
concElement = this.fmxClassMap.get(fullyQualifiedFilename) as Famix.ParametricClass;
|
|
416
|
+
} else if (concreteElement instanceof Famix.ParametricInterface) {
|
|
417
|
+
concElement = this.fmxInterfaceMap.get(fullyQualifiedFilename) as Famix.ParametricInterface;
|
|
418
|
+
} else if (concreteElement instanceof Famix.ParametricFunction) {
|
|
419
|
+
concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricFunction;
|
|
420
|
+
} else { // if (concreteElement instanceof Famix.ParametricMethod) {
|
|
421
|
+
concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricMethod;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return concElement;
|
|
425
|
+
}
|
|
426
|
+
|
|
285
427
|
/**
|
|
286
428
|
* Creates a Famix property
|
|
287
429
|
* @param property A property
|
|
@@ -290,7 +432,7 @@ export class EntityDictionary {
|
|
|
290
432
|
public createFamixProperty(property: PropertyDeclaration | PropertySignature): Famix.Property {
|
|
291
433
|
const fmxProperty = new Famix.Property();
|
|
292
434
|
const isSignature = property instanceof PropertySignature;
|
|
293
|
-
fmxProperty.
|
|
435
|
+
fmxProperty.name = property.getName();
|
|
294
436
|
|
|
295
437
|
let propTypeName = this.UNKNOWN_VALUE;
|
|
296
438
|
try {
|
|
@@ -300,26 +442,44 @@ export class EntityDictionary {
|
|
|
300
442
|
}
|
|
301
443
|
|
|
302
444
|
const fmxType = this.createOrGetFamixType(propTypeName, property);
|
|
303
|
-
fmxProperty.
|
|
445
|
+
fmxProperty.declaredType = fmxType;
|
|
446
|
+
|
|
447
|
+
// add the visibility (public, private, etc.) to the fmxProperty
|
|
448
|
+
fmxProperty.visibility = "";
|
|
449
|
+
|
|
450
|
+
property.getModifiers().forEach(m => {
|
|
451
|
+
switch (m.getText()) {
|
|
452
|
+
case Scope.Public:
|
|
453
|
+
fmxProperty.visibility = "public";
|
|
454
|
+
break;
|
|
455
|
+
case Scope.Protected:
|
|
456
|
+
fmxProperty.visibility = "protected";
|
|
457
|
+
break;
|
|
458
|
+
case Scope.Private:
|
|
459
|
+
fmxProperty.visibility = "private";
|
|
460
|
+
break;
|
|
461
|
+
case "static":
|
|
462
|
+
fmxProperty.isClassSide = true;
|
|
463
|
+
break;
|
|
464
|
+
case "readonly":
|
|
465
|
+
fmxProperty.readOnly = true;
|
|
466
|
+
break;
|
|
467
|
+
default:
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
});
|
|
304
471
|
|
|
305
|
-
property.getModifiers().forEach(m => fmxProperty.addModifier(m.getText()));
|
|
306
472
|
if (!isSignature && property.getExclamationTokenNode()) {
|
|
307
|
-
fmxProperty.
|
|
473
|
+
fmxProperty.isDefinitelyAssigned = true;
|
|
308
474
|
}
|
|
309
475
|
if (property.getQuestionTokenNode()) {
|
|
310
|
-
fmxProperty.
|
|
476
|
+
fmxProperty.isOptional = true;
|
|
311
477
|
}
|
|
312
478
|
if (property.getName().substring(0, 1) === "#") {
|
|
313
|
-
fmxProperty.
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (fmxProperty.getModifiers().has("static")) {
|
|
317
|
-
fmxProperty.setIsClassSide(true);
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
fmxProperty.setIsClassSide(false);
|
|
479
|
+
fmxProperty.isJavaScriptPrivate = true;
|
|
321
480
|
}
|
|
322
481
|
|
|
482
|
+
initFQN(property, fmxProperty);
|
|
323
483
|
this.makeFamixIndexFileAnchor(property, fmxProperty);
|
|
324
484
|
|
|
325
485
|
this.famixRep.addElement(fmxProperty);
|
|
@@ -335,89 +495,103 @@ export class EntityDictionary {
|
|
|
335
495
|
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
336
496
|
* @returns The Famix model of the method or the accessor
|
|
337
497
|
*/
|
|
338
|
-
public
|
|
339
|
-
let fmxMethod: Famix.Method | Famix.Accessor;
|
|
340
|
-
if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
|
|
341
|
-
fmxMethod = new Famix.Accessor();
|
|
342
|
-
const isGetter = method instanceof GetAccessorDeclaration;
|
|
343
|
-
const isSetter = method instanceof SetAccessorDeclaration;
|
|
344
|
-
if (isGetter) {(fmxMethod as Famix.Accessor).setKind("getter");}
|
|
345
|
-
if (isSetter) {(fmxMethod as Famix.Accessor).setKind("setter");}
|
|
346
|
-
this.famixRep.addElement(fmxMethod);
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
fmxMethod = new Famix.Method();
|
|
350
|
-
this.famixRep.addElement(fmxMethod);
|
|
351
|
-
}
|
|
352
|
-
const isConstructor = method instanceof ConstructorDeclaration;
|
|
353
|
-
const isSignature = method instanceof MethodSignature;
|
|
498
|
+
public createOrGetFamixMethod(method: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration, currentCC: { [key: string]: number }): Famix.Method | Famix.Accessor | Famix.ParametricMethod {
|
|
499
|
+
let fmxMethod: Famix.Method | Famix.Accessor | Famix.ParametricMethod;
|
|
354
500
|
const isGeneric = method.getTypeParameters().length > 0;
|
|
355
|
-
|
|
501
|
+
const functionFullyQualifiedName = FQNFunctions.getFQN(method);
|
|
502
|
+
if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
|
|
503
|
+
|
|
504
|
+
if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
|
|
505
|
+
fmxMethod = new Famix.Accessor();
|
|
506
|
+
const isGetter = method instanceof GetAccessorDeclaration;
|
|
507
|
+
const isSetter = method instanceof SetAccessorDeclaration;
|
|
508
|
+
if (isGetter) {(fmxMethod as Famix.Accessor).kind = "getter";}
|
|
509
|
+
if (isSetter) {(fmxMethod as Famix.Accessor).kind = "setter";}
|
|
510
|
+
this.famixRep.addElement(fmxMethod);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
if (isGeneric) {
|
|
514
|
+
fmxMethod = new Famix.ParametricMethod();
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
fmxMethod = new Famix.Method();
|
|
518
|
+
}
|
|
519
|
+
this.famixRep.addElement(fmxMethod);
|
|
520
|
+
}
|
|
521
|
+
const isConstructor = method instanceof ConstructorDeclaration;
|
|
522
|
+
const isSignature = method instanceof MethodSignature;
|
|
523
|
+
|
|
524
|
+
let isAbstract = false;
|
|
525
|
+
let isStatic = false;
|
|
526
|
+
if (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
|
|
527
|
+
isAbstract = method.isAbstract();
|
|
528
|
+
isStatic = method.isStatic();
|
|
529
|
+
}
|
|
356
530
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
531
|
+
if (isConstructor) {(fmxMethod as Famix.Accessor).kind = "constructor";}
|
|
532
|
+
fmxMethod.isAbstract = isAbstract;
|
|
533
|
+
fmxMethod.isClassSide = isStatic;
|
|
534
|
+
fmxMethod.isPrivate = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'private')) !== undefined : false;
|
|
535
|
+
fmxMethod.isProtected = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'protected')) !== undefined : false;
|
|
536
|
+
fmxMethod.signature = Helpers.computeSignature(method.getText());
|
|
363
537
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
538
|
+
let methodName: string;
|
|
539
|
+
if (isConstructor) {
|
|
540
|
+
methodName = "constructor";
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
methodName = (method as MethodDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration).getName();
|
|
544
|
+
}
|
|
545
|
+
fmxMethod.name = methodName;
|
|
370
546
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
methodName = (method as MethodDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration).getName();
|
|
377
|
-
}
|
|
378
|
-
fmxMethod.setName(methodName);
|
|
547
|
+
if (!isConstructor) {
|
|
548
|
+
if (method.getName().substring(0, 1) === "#") {
|
|
549
|
+
fmxMethod.isPrivate = true;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
379
552
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
553
|
+
if (!fmxMethod.isPrivate && !fmxMethod.isProtected) {
|
|
554
|
+
fmxMethod.isPublic = true;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
fmxMethod.isPublic = false;
|
|
383
558
|
}
|
|
384
|
-
}
|
|
385
559
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
560
|
+
if (!isSignature) {
|
|
561
|
+
fmxMethod.cyclomaticComplexity = currentCC[fmxMethod.name];
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
fmxMethod.cyclomaticComplexity = 0;
|
|
565
|
+
}
|
|
392
566
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
567
|
+
let methodTypeName = this.UNKNOWN_VALUE;
|
|
568
|
+
try {
|
|
569
|
+
methodTypeName = method.getReturnType().getText().trim();
|
|
570
|
+
} catch (error) {
|
|
571
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of method: ${fmxMethod.name}. Continuing...`);
|
|
572
|
+
}
|
|
399
573
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
574
|
+
const fmxType = this.createOrGetFamixType(methodTypeName, method);
|
|
575
|
+
fmxMethod.declaredType = fmxType;
|
|
576
|
+
fmxMethod.numberOfLinesOfCode = method.getEndLineNumber() - method.getStartLineNumber();
|
|
577
|
+
const parameters = method.getParameters();
|
|
578
|
+
fmxMethod.numberOfParameters = parameters.length;
|
|
406
579
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
580
|
+
if (!isSignature) {
|
|
581
|
+
fmxMethod.numberOfStatements = method.getStatements().length;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
fmxMethod.numberOfStatements = 0;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
initFQN(method, fmxMethod);
|
|
588
|
+
this.makeFamixIndexFileAnchor(method, fmxMethod);
|
|
412
589
|
|
|
413
|
-
|
|
414
|
-
fmxMethod.setNumberOfStatements(method.getStatements().length);
|
|
590
|
+
this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxMethod);
|
|
415
591
|
}
|
|
416
592
|
else {
|
|
417
|
-
fmxMethod.
|
|
593
|
+
fmxMethod = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Method | Famix.Accessor | Famix.ParametricMethod);
|
|
418
594
|
}
|
|
419
|
-
|
|
420
|
-
this.makeFamixIndexFileAnchor(method, fmxMethod);
|
|
421
595
|
|
|
422
596
|
this.fmxElementObjectMap.set(fmxMethod,method);
|
|
423
597
|
|
|
@@ -430,38 +604,54 @@ export class EntityDictionary {
|
|
|
430
604
|
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
431
605
|
* @returns The Famix model of the function
|
|
432
606
|
*/
|
|
433
|
-
public
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
607
|
+
public createOrGetFamixFunction(func: FunctionDeclaration | FunctionExpression, currentCC: { [key: string]: number }): Famix.Function | Famix.ParametricFunction {
|
|
608
|
+
let fmxFunction: Famix.Function | Famix.ParametricFunction;
|
|
609
|
+
const isGeneric = func.getTypeParameters().length > 0;
|
|
610
|
+
const functionFullyQualifiedName = FQNFunctions.getFQN(func);
|
|
611
|
+
if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
|
|
612
|
+
if (isGeneric) {
|
|
613
|
+
fmxFunction = new Famix.ParametricFunction();
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
fmxFunction = new Famix.Function();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const name = func.getName();
|
|
620
|
+
if (name) {
|
|
621
|
+
fmxFunction.name = name;
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
fmxFunction.name = "anonymous";
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
fmxFunction.signature = Helpers.computeSignature(func.getText());
|
|
628
|
+
fmxFunction.cyclomaticComplexity = currentCC[fmxFunction.name];
|
|
629
|
+
fmxFunction.fullyQualifiedName = functionFullyQualifiedName;
|
|
630
|
+
|
|
631
|
+
let functionTypeName = this.UNKNOWN_VALUE;
|
|
632
|
+
try {
|
|
633
|
+
functionTypeName = func.getReturnType().getText().trim();
|
|
634
|
+
} catch (error) {
|
|
635
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const fmxType = this.createOrGetFamixType(functionTypeName, func);
|
|
639
|
+
fmxFunction.declaredType = fmxType;
|
|
640
|
+
fmxFunction.numberOfLinesOfCode = func.getEndLineNumber() - func.getStartLineNumber();
|
|
641
|
+
const parameters = func.getParameters();
|
|
642
|
+
fmxFunction.numberOfParameters = parameters.length;
|
|
643
|
+
fmxFunction.numberOfStatements = func.getStatements().length;
|
|
644
|
+
this.makeFamixIndexFileAnchor(func, fmxFunction);
|
|
645
|
+
|
|
646
|
+
this.famixRep.addElement(fmxFunction);
|
|
647
|
+
|
|
648
|
+
this.fmxElementObjectMap.set(fmxFunction,func);
|
|
649
|
+
|
|
650
|
+
this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxFunction);
|
|
437
651
|
}
|
|
438
652
|
else {
|
|
439
|
-
fmxFunction.
|
|
653
|
+
fmxFunction = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Function | Famix.ParametricFunction);
|
|
440
654
|
}
|
|
441
|
-
fmxFunction.setSignature(Helpers.computeSignature(func.getText()));
|
|
442
|
-
fmxFunction.setCyclomaticComplexity(currentCC[fmxFunction.getName()]);
|
|
443
|
-
const isGeneric = func.getTypeParameters().length > 0;
|
|
444
|
-
fmxFunction.setIsGeneric(isGeneric);
|
|
445
|
-
|
|
446
|
-
let functionTypeName = this.UNKNOWN_VALUE;
|
|
447
|
-
try {
|
|
448
|
-
functionTypeName = func.getReturnType().getText().trim();
|
|
449
|
-
} catch (error) {
|
|
450
|
-
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const fmxType = this.createOrGetFamixType(functionTypeName, func);
|
|
454
|
-
fmxFunction.setDeclaredType(fmxType);
|
|
455
|
-
fmxFunction.setNumberOfLinesOfCode(func.getEndLineNumber() - func.getStartLineNumber());
|
|
456
|
-
const parameters = func.getParameters();
|
|
457
|
-
fmxFunction.setNumberOfParameters(parameters.length);
|
|
458
|
-
fmxFunction.setNumberOfStatements(func.getStatements().length);
|
|
459
|
-
|
|
460
|
-
this.makeFamixIndexFileAnchor(func, fmxFunction);
|
|
461
|
-
|
|
462
|
-
this.famixRep.addElement(fmxFunction);
|
|
463
|
-
|
|
464
|
-
this.fmxElementObjectMap.set(fmxFunction,func);
|
|
465
655
|
|
|
466
656
|
return fmxFunction;
|
|
467
657
|
}
|
|
@@ -482,9 +672,10 @@ export class EntityDictionary {
|
|
|
482
672
|
}
|
|
483
673
|
|
|
484
674
|
const fmxType = this.createOrGetFamixType(paramTypeName, param);
|
|
485
|
-
fmxParam.
|
|
486
|
-
fmxParam.
|
|
675
|
+
fmxParam.declaredType = fmxType;
|
|
676
|
+
fmxParam.name = param.getName();
|
|
487
677
|
|
|
678
|
+
initFQN(param, fmxParam);
|
|
488
679
|
this.makeFamixIndexFileAnchor(param, fmxParam);
|
|
489
680
|
|
|
490
681
|
this.famixRep.addElement(fmxParam);
|
|
@@ -500,9 +691,11 @@ export class EntityDictionary {
|
|
|
500
691
|
* @returns The Famix model of the type parameter
|
|
501
692
|
*/
|
|
502
693
|
public createFamixParameterType(tp: TypeParameterDeclaration): Famix.ParameterType {
|
|
694
|
+
|
|
503
695
|
const fmxParameterType = new Famix.ParameterType();
|
|
504
|
-
|
|
505
|
-
|
|
696
|
+
|
|
697
|
+
fmxParameterType.name = tp.getName();
|
|
698
|
+
initFQN(tp, fmxParameterType);
|
|
506
699
|
this.makeFamixIndexFileAnchor(tp, fmxParameterType);
|
|
507
700
|
|
|
508
701
|
this.famixRep.addElement(fmxParameterType);
|
|
@@ -512,6 +705,78 @@ export class EntityDictionary {
|
|
|
512
705
|
return fmxParameterType;
|
|
513
706
|
}
|
|
514
707
|
|
|
708
|
+
/**
|
|
709
|
+
* Creates a Famix type parameter
|
|
710
|
+
* @param tp A type parameter
|
|
711
|
+
* @returns The Famix model of the type parameter
|
|
712
|
+
*/
|
|
713
|
+
public createOrGetFamixConcreteType(param: TypeNode): Famix.ParameterType | Famix.PrimitiveType | Famix.Class | Famix.Interface {
|
|
714
|
+
const typeParameterDeclaration = param.getSymbol()?.getDeclarations()[0] as TypeParameterDeclaration;
|
|
715
|
+
const parameterTypeName : string = param.getText();
|
|
716
|
+
let fmxParameterType: Famix.Type | Famix.Class | Famix.Interface | undefined = undefined;
|
|
717
|
+
|
|
718
|
+
let isClassOrInterface = false;
|
|
719
|
+
if (this.fmxClassMap.has(parameterTypeName)){
|
|
720
|
+
this.fmxClassMap.forEach((obj, name) => {
|
|
721
|
+
if(obj instanceof Famix.ParametricClass){
|
|
722
|
+
if (name === param.getText() && obj.genericParameters.size>0) {
|
|
723
|
+
fmxParameterType = obj;
|
|
724
|
+
isClassOrInterface = true;
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
if (name === param.getText()) {
|
|
728
|
+
fmxParameterType = obj;
|
|
729
|
+
isClassOrInterface = true;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
})
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (this.fmxInterfaceMap.has(parameterTypeName)){
|
|
736
|
+
this.fmxInterfaceMap.forEach((obj, name) => {
|
|
737
|
+
if(obj instanceof Famix.ParametricInterface){
|
|
738
|
+
if (name === param.getText() && obj.genericParameters.size>0) {
|
|
739
|
+
fmxParameterType = obj;
|
|
740
|
+
isClassOrInterface = true;
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
if (name === param.getText()) {
|
|
744
|
+
fmxParameterType = obj;
|
|
745
|
+
isClassOrInterface = true;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
})
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if(!isClassOrInterface){
|
|
752
|
+
if (!this.fmxTypeMap.has(parameterTypeName)) {
|
|
753
|
+
if (parameterTypeName === "number" || parameterTypeName === "string" || parameterTypeName === "boolean" || parameterTypeName === "bigint" || parameterTypeName === "symbol" || parameterTypeName === "undefined" || parameterTypeName === "null" || parameterTypeName === "any" || parameterTypeName === "unknown" || parameterTypeName === "never" || parameterTypeName === "void") {
|
|
754
|
+
fmxParameterType = new Famix.PrimitiveType();
|
|
755
|
+
fmxParameterType.isStub = true;
|
|
756
|
+
} else {
|
|
757
|
+
fmxParameterType = new Famix.ParameterType();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
fmxParameterType.name = parameterTypeName;
|
|
761
|
+
this.famixRep.addElement(fmxParameterType);
|
|
762
|
+
this.fmxTypeMap.set(parameterTypeName, fmxParameterType);
|
|
763
|
+
this.fmxElementObjectMap.set(fmxParameterType,typeParameterDeclaration);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
const result = this.fmxTypeMap.get(parameterTypeName);
|
|
767
|
+
if (result) {
|
|
768
|
+
fmxParameterType = result;
|
|
769
|
+
} else {
|
|
770
|
+
throw new Error(`Famix type ${parameterTypeName} is not found in the Type map.`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (!fmxParameterType) {
|
|
775
|
+
throw new Error(`fmxParameterType was undefined for parameterTypeName ${parameterTypeName}`);
|
|
776
|
+
}
|
|
777
|
+
return fmxParameterType;
|
|
778
|
+
}
|
|
779
|
+
|
|
515
780
|
/**
|
|
516
781
|
* Creates a Famix variable
|
|
517
782
|
* @param variable A variable
|
|
@@ -519,24 +784,24 @@ export class EntityDictionary {
|
|
|
519
784
|
*/
|
|
520
785
|
public createFamixVariable(variable: VariableDeclaration): Famix.Variable {
|
|
521
786
|
const fmxVariable = new Famix.Variable();
|
|
522
|
-
|
|
787
|
+
|
|
523
788
|
let variableTypeName = this.UNKNOWN_VALUE;
|
|
524
789
|
try {
|
|
525
790
|
variableTypeName = variable.getType().getText().trim();
|
|
526
791
|
} catch (error) {
|
|
527
792
|
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for variable: ${variable.getName()}. Continuing...`);
|
|
528
793
|
}
|
|
529
|
-
|
|
794
|
+
|
|
530
795
|
const fmxType = this.createOrGetFamixType(variableTypeName, variable);
|
|
531
|
-
fmxVariable.
|
|
532
|
-
fmxVariable.
|
|
533
|
-
|
|
796
|
+
fmxVariable.declaredType = fmxType;
|
|
797
|
+
fmxVariable.name = variable.getName();
|
|
798
|
+
initFQN(variable, fmxVariable);
|
|
534
799
|
this.makeFamixIndexFileAnchor(variable, fmxVariable);
|
|
535
|
-
|
|
800
|
+
|
|
536
801
|
this.famixRep.addElement(fmxVariable);
|
|
537
|
-
|
|
802
|
+
|
|
538
803
|
this.fmxElementObjectMap.set(fmxVariable,variable);
|
|
539
|
-
|
|
804
|
+
|
|
540
805
|
return fmxVariable;
|
|
541
806
|
}
|
|
542
807
|
|
|
@@ -547,8 +812,8 @@ export class EntityDictionary {
|
|
|
547
812
|
*/
|
|
548
813
|
public createFamixEnum(enumEntity: EnumDeclaration): Famix.Enum {
|
|
549
814
|
const fmxEnum = new Famix.Enum();
|
|
550
|
-
fmxEnum.
|
|
551
|
-
|
|
815
|
+
fmxEnum.name = enumEntity.getName();
|
|
816
|
+
initFQN(enumEntity, fmxEnum);
|
|
552
817
|
this.makeFamixIndexFileAnchor(enumEntity, fmxEnum);
|
|
553
818
|
|
|
554
819
|
this.famixRep.addElement(fmxEnum);
|
|
@@ -574,9 +839,9 @@ export class EntityDictionary {
|
|
|
574
839
|
}
|
|
575
840
|
|
|
576
841
|
const fmxType = this.createOrGetFamixType(enumValueTypeName, enumMember);
|
|
577
|
-
fmxEnumValue.
|
|
578
|
-
fmxEnumValue.
|
|
579
|
-
|
|
842
|
+
fmxEnumValue.declaredType = fmxType;
|
|
843
|
+
fmxEnumValue.name = enumMember.getName();
|
|
844
|
+
initFQN(enumMember, fmxEnumValue);
|
|
580
845
|
this.makeFamixIndexFileAnchor(enumMember, fmxEnumValue);
|
|
581
846
|
|
|
582
847
|
this.famixRep.addElement(fmxEnumValue);
|
|
@@ -597,12 +862,12 @@ export class EntityDictionary {
|
|
|
597
862
|
const decoratorName = "@" + decorator.getName();
|
|
598
863
|
const decoratorExpression = decorator.getText().substring(1);
|
|
599
864
|
|
|
600
|
-
fmxDecorator.
|
|
601
|
-
fmxDecorator.
|
|
865
|
+
fmxDecorator.name = decoratorName;
|
|
866
|
+
fmxDecorator.decoratorExpression = decoratorExpression;
|
|
602
867
|
const decoratedEntityFullyQualifiedName = FQNFunctions.getFQN(decoratedEntity);
|
|
603
868
|
const fmxDecoratedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(decoratedEntityFullyQualifiedName) as Famix.NamedEntity;
|
|
604
|
-
fmxDecorator.
|
|
605
|
-
|
|
869
|
+
fmxDecorator.decoratedEntity = fmxDecoratedEntity;
|
|
870
|
+
initFQN(decorator, fmxDecorator);
|
|
606
871
|
this.makeFamixIndexFileAnchor(decorator, fmxDecorator);
|
|
607
872
|
|
|
608
873
|
this.famixRep.addElement(fmxDecorator);
|
|
@@ -620,10 +885,10 @@ export class EntityDictionary {
|
|
|
620
885
|
* @returns The Famix model of the comment
|
|
621
886
|
*/
|
|
622
887
|
public createFamixComment(comment: CommentRange, fmxScope: Famix.NamedEntity, isJSDoc: boolean): Famix.Comment {
|
|
623
|
-
logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.
|
|
888
|
+
logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.name}.`);
|
|
624
889
|
const fmxComment = new Famix.Comment();
|
|
625
|
-
fmxComment.
|
|
626
|
-
fmxComment.
|
|
890
|
+
fmxComment.container = fmxScope; // adds comment to the container's comments collection
|
|
891
|
+
fmxComment.isJSDoc = isJSDoc;
|
|
627
892
|
|
|
628
893
|
this.makeFamixIndexFileAnchor(comment, fmxComment);
|
|
629
894
|
|
|
@@ -640,19 +905,23 @@ export class EntityDictionary {
|
|
|
640
905
|
* @param element A ts-morph element
|
|
641
906
|
* @returns The Famix model of the type
|
|
642
907
|
*/
|
|
643
|
-
public createOrGetFamixType(typeName: string, element:
|
|
644
|
-
let fmxType: Famix.Type | Famix.PrimitiveType | Famix.
|
|
908
|
+
public createOrGetFamixType(typeName: string, element: TypeDeclaration): Famix.Type | Famix.PrimitiveType | Famix.ParameterType {
|
|
909
|
+
let fmxType: Famix.Type | Famix.PrimitiveType | Famix.ParameterType;
|
|
645
910
|
let isPrimitiveType = false;
|
|
646
|
-
let
|
|
911
|
+
let isParameterType = false;
|
|
647
912
|
|
|
648
913
|
logger.debug("Creating (or getting) type: '" + typeName + "' of element: " + element?.getText() + " of kind: " + element?.getKindName());
|
|
649
|
-
let ancestor: Famix.ContainerEntity;
|
|
914
|
+
let ancestor: Famix.ContainerEntity | undefined = undefined;
|
|
650
915
|
if (element !== undefined) {
|
|
651
916
|
const typeAncestor = Helpers.findTypeAncestor(element);
|
|
917
|
+
if (!typeAncestor) {
|
|
918
|
+
throw new Error(`Ancestor not found for element ${element.getText()}.`);
|
|
919
|
+
}
|
|
652
920
|
const ancestorFullyQualifiedName = FQNFunctions.getFQN(typeAncestor);
|
|
653
921
|
ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
|
|
654
922
|
if (!ancestor) {
|
|
655
|
-
|
|
923
|
+
logger.debug(`Ancestor ${FQNFunctions.getFQN(typeAncestor)} not found. Adding the new type.`);
|
|
924
|
+
ancestor = this.createOrGetFamixType(typeAncestor.getText(), typeAncestor as TypeDeclaration);
|
|
656
925
|
}
|
|
657
926
|
}
|
|
658
927
|
|
|
@@ -661,32 +930,35 @@ export class EntityDictionary {
|
|
|
661
930
|
}
|
|
662
931
|
|
|
663
932
|
if(!isPrimitiveType && typeName.includes("<") && typeName.includes(">") && !(typeName.includes("=>"))) {
|
|
664
|
-
|
|
933
|
+
isParameterType = true;
|
|
665
934
|
}
|
|
666
935
|
|
|
667
936
|
if (!this.fmxTypeMap.has(typeName)) {
|
|
668
937
|
if (isPrimitiveType) {
|
|
669
938
|
fmxType = new Famix.PrimitiveType();
|
|
670
|
-
fmxType.
|
|
939
|
+
fmxType.isStub = true;
|
|
671
940
|
}
|
|
672
|
-
else if (
|
|
673
|
-
fmxType = new Famix.
|
|
941
|
+
else if (isParameterType) {
|
|
942
|
+
fmxType = new Famix.ParameterType();
|
|
674
943
|
const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">")).split(",").map(s => s.trim());
|
|
675
944
|
const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
|
|
676
945
|
parameterTypeNames.forEach(parameterTypeName => {
|
|
677
946
|
const fmxParameterType = this.createOrGetFamixType(parameterTypeName, element);
|
|
678
|
-
(fmxType as Famix.
|
|
947
|
+
(fmxType as Famix.ParameterType).addArgument(fmxParameterType);
|
|
679
948
|
});
|
|
680
949
|
const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
|
|
681
|
-
(fmxType as Famix.
|
|
950
|
+
(fmxType as Famix.ParameterType).baseType = fmxBaseType;
|
|
682
951
|
}
|
|
683
952
|
else {
|
|
684
953
|
fmxType = new Famix.Type();
|
|
685
954
|
}
|
|
686
955
|
|
|
687
|
-
fmxType.
|
|
688
|
-
|
|
689
|
-
|
|
956
|
+
fmxType.name = typeName;
|
|
957
|
+
if (!ancestor) {
|
|
958
|
+
throw new Error(`Ancestor not found for type ${typeName}.`);
|
|
959
|
+
}
|
|
960
|
+
fmxType.container = ancestor;
|
|
961
|
+
initFQN(element, fmxType);
|
|
690
962
|
this.makeFamixIndexFileAnchor(element, fmxType);
|
|
691
963
|
|
|
692
964
|
this.famixRep.addElement(fmxType);
|
|
@@ -694,7 +966,12 @@ export class EntityDictionary {
|
|
|
694
966
|
this.fmxTypeMap.set(typeName, fmxType);
|
|
695
967
|
}
|
|
696
968
|
else {
|
|
697
|
-
|
|
969
|
+
const result = this.fmxTypeMap.get(typeName);
|
|
970
|
+
if (result) {
|
|
971
|
+
fmxType = result;
|
|
972
|
+
} else {
|
|
973
|
+
throw new Error(`Famix type ${typeName} is not found in the Type map.`);
|
|
974
|
+
}
|
|
698
975
|
}
|
|
699
976
|
|
|
700
977
|
this.fmxElementObjectMap.set(fmxType,element);
|
|
@@ -709,15 +986,23 @@ export class EntityDictionary {
|
|
|
709
986
|
*/
|
|
710
987
|
public createFamixAccess(node: Identifier, id: number): void {
|
|
711
988
|
const fmxVar = this.famixRep.getFamixEntityById(id) as Famix.StructuralEntity;
|
|
989
|
+
if (!fmxVar) {
|
|
990
|
+
throw new Error(`Famix entity with id ${id} not found, for node ${node.getText()} in ${node.getSourceFile().getBaseName()} at line ${node.getStartLineNumber()}.`);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
logger.debug(`Creating FamixAccess. Node: [${node.getKindName()}] '${node.getText()}' at line ${node.getStartLineNumber()} in ${node.getSourceFile().getBaseName()}, id: ${id} refers to fmxVar '${fmxVar.fullyQualifiedName}'.`);
|
|
994
|
+
|
|
712
995
|
const nodeReferenceAncestor = Helpers.findAncestor(node);
|
|
713
996
|
const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
|
|
714
|
-
|
|
997
|
+
let accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
|
|
998
|
+
if (!accessor) {
|
|
999
|
+
logger.error(`Ancestor ${ancestorFullyQualifiedName} of kind ${nodeReferenceAncestor.getKindName()} not found.`);
|
|
1000
|
+
// accessor = this.createOrGetFamixType(ancestorFullyQualifiedName, nodeReferenceAncestor as TypeDeclaration);
|
|
1001
|
+
}
|
|
715
1002
|
|
|
716
1003
|
const fmxAccess = new Famix.Access();
|
|
717
|
-
fmxAccess.
|
|
718
|
-
fmxAccess.
|
|
719
|
-
|
|
720
|
-
this.makeFamixIndexFileAnchor(node, fmxAccess);
|
|
1004
|
+
fmxAccess.accessor = accessor;
|
|
1005
|
+
fmxAccess.variable = fmxVar;
|
|
721
1006
|
|
|
722
1007
|
this.famixRep.addElement(fmxAccess);
|
|
723
1008
|
|
|
@@ -739,12 +1024,10 @@ export class EntityDictionary {
|
|
|
739
1024
|
const receiver = this.famixRep.getFamixEntityByFullyQualifiedName(receiverFullyQualifiedName) as Famix.NamedEntity;
|
|
740
1025
|
|
|
741
1026
|
const fmxInvocation = new Famix.Invocation();
|
|
742
|
-
fmxInvocation.
|
|
743
|
-
fmxInvocation.
|
|
1027
|
+
fmxInvocation.sender = sender;
|
|
1028
|
+
fmxInvocation.receiver = receiver;
|
|
744
1029
|
fmxInvocation.addCandidate(fmxMethodOrFunction);
|
|
745
|
-
fmxInvocation.
|
|
746
|
-
|
|
747
|
-
this.makeFamixIndexFileAnchor(node, fmxInvocation);
|
|
1030
|
+
fmxInvocation.signature = fmxMethodOrFunction.signature;
|
|
748
1031
|
|
|
749
1032
|
this.famixRep.addElement(fmxInvocation);
|
|
750
1033
|
|
|
@@ -761,19 +1044,25 @@ export class EntityDictionary {
|
|
|
761
1044
|
// const clsName = cls.getName();
|
|
762
1045
|
const classFullyQualifiedName = FQNFunctions.getFQN(cls);
|
|
763
1046
|
logger.debug(`createFamixInheritance: classFullyQualifiedName: class fqn = ${classFullyQualifiedName}`);
|
|
764
|
-
let subClass: Famix.Class | Famix.Interface;
|
|
1047
|
+
let subClass: Famix.Class | Famix.Interface | undefined;
|
|
765
1048
|
if (cls instanceof ClassDeclaration) {
|
|
766
1049
|
subClass = this.fmxClassMap.get(classFullyQualifiedName);
|
|
767
1050
|
}
|
|
768
1051
|
else {
|
|
769
1052
|
subClass = this.fmxInterfaceMap.get(classFullyQualifiedName);
|
|
770
1053
|
}
|
|
1054
|
+
if (!subClass) {
|
|
1055
|
+
throw new Error(`Subclass ${classFullyQualifiedName} not found in Class or Interface maps.`);
|
|
1056
|
+
}
|
|
771
1057
|
|
|
772
|
-
let inhClassName: string;
|
|
1058
|
+
let inhClassName: string | undefined;
|
|
773
1059
|
let inhClassFullyQualifiedName: string;
|
|
774
|
-
let superClass: Famix.Class | Famix.Interface;
|
|
1060
|
+
let superClass: Famix.Class | Famix.Interface | undefined;
|
|
775
1061
|
if (inhClass instanceof ClassDeclaration || inhClass instanceof InterfaceDeclaration) {
|
|
776
1062
|
inhClassName = inhClass.getName();
|
|
1063
|
+
if (!inhClassName) {
|
|
1064
|
+
throw new Error(`Inherited class or interface name not found for ${inhClass.getText()}.`);
|
|
1065
|
+
}
|
|
777
1066
|
inhClassFullyQualifiedName = FQNFunctions.getFQN(inhClass);
|
|
778
1067
|
if (inhClass instanceof ClassDeclaration) {
|
|
779
1068
|
superClass = this.fmxClassMap.get(inhClassFullyQualifiedName);
|
|
@@ -781,6 +1070,9 @@ export class EntityDictionary {
|
|
|
781
1070
|
else {
|
|
782
1071
|
superClass = this.fmxInterfaceMap.get(inhClassFullyQualifiedName);
|
|
783
1072
|
}
|
|
1073
|
+
if (!superClass) {
|
|
1074
|
+
throw new Error(`Superclass ${classFullyQualifiedName} not found in Class or Interface maps.`);
|
|
1075
|
+
}
|
|
784
1076
|
}
|
|
785
1077
|
else {
|
|
786
1078
|
// inhClass is an ExpressionWithTypeArguments
|
|
@@ -801,24 +1093,31 @@ export class EntityDictionary {
|
|
|
801
1093
|
|
|
802
1094
|
this.fmxElementObjectMap.set(superClass,inhClass);
|
|
803
1095
|
|
|
804
|
-
superClass.
|
|
805
|
-
superClass.
|
|
806
|
-
superClass.
|
|
1096
|
+
superClass.name = inhClassName;
|
|
1097
|
+
superClass.fullyQualifiedName = inhClassFullyQualifiedName;
|
|
1098
|
+
superClass.isStub = true;
|
|
807
1099
|
|
|
808
1100
|
this.makeFamixIndexFileAnchor(inhClass, superClass);
|
|
809
1101
|
|
|
810
1102
|
this.famixRep.addElement(superClass);
|
|
811
1103
|
}
|
|
812
1104
|
|
|
813
|
-
fmxInheritance.
|
|
814
|
-
fmxInheritance.
|
|
815
|
-
|
|
816
|
-
this.makeFamixIndexFileAnchor(null, fmxInheritance);
|
|
1105
|
+
fmxInheritance.subclass = subClass;
|
|
1106
|
+
fmxInheritance.superclass = superClass;
|
|
817
1107
|
|
|
818
1108
|
this.famixRep.addElement(fmxInheritance);
|
|
819
1109
|
|
|
820
|
-
|
|
1110
|
+
// We don't map inheritance to the source code element because there are two elements (super, sub)
|
|
1111
|
+
// this.fmxElementObjectMap.set(fmxInheritance, null);
|
|
1112
|
+
|
|
1113
|
+
}
|
|
821
1114
|
|
|
1115
|
+
public createFamixImportClause(importedEntity: Famix.NamedEntity, importingEntity: Famix.Module) {
|
|
1116
|
+
const fmxImportClause = new Famix.ImportClause();
|
|
1117
|
+
fmxImportClause.importedEntity = importedEntity;
|
|
1118
|
+
fmxImportClause.importingEntity = importingEntity;
|
|
1119
|
+
importingEntity.addOutgoingImport(fmxImportClause);
|
|
1120
|
+
this.famixRep.addElement(fmxImportClause);
|
|
822
1121
|
}
|
|
823
1122
|
|
|
824
1123
|
/**
|
|
@@ -831,12 +1130,12 @@ export class EntityDictionary {
|
|
|
831
1130
|
* @param isInExports A boolean indicating if the imported entity is in the exports
|
|
832
1131
|
* @param isDefaultExport A boolean indicating if the imported entity is a default export
|
|
833
1132
|
*/
|
|
834
|
-
public
|
|
835
|
-
const {importDeclaration, importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
|
|
1133
|
+
public oldCreateFamixImportClause(importClauseInfo: {importDeclaration?: ImportDeclaration | ImportEqualsDeclaration, importerSourceFile: SourceFile, moduleSpecifierFilePath: string, importElement: ImportSpecifier | Identifier, isInExports: boolean, isDefaultExport: boolean}): void {
|
|
1134
|
+
const {importDeclaration, importerSourceFile: importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
|
|
836
1135
|
logger.debug(`createFamixImportClause: Creating import clause:`);
|
|
837
1136
|
const fmxImportClause = new Famix.ImportClause();
|
|
838
1137
|
|
|
839
|
-
let importedEntity: Famix.NamedEntity | Famix.StructuralEntity;
|
|
1138
|
+
let importedEntity: Famix.NamedEntity | Famix.StructuralEntity | undefined = undefined;
|
|
840
1139
|
let importedEntityName: string;
|
|
841
1140
|
|
|
842
1141
|
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
@@ -844,10 +1143,12 @@ export class EntityDictionary {
|
|
|
844
1143
|
const absolutePath = path.normalize(moduleSpecifierFilePath);
|
|
845
1144
|
// convert the path and remove any windows backslashes introduced by path.normalize
|
|
846
1145
|
const pathInProject: string = this.convertToRelativePath(absolutePath, absolutePathProject).replace(/\\/g, "/");
|
|
847
|
-
|
|
848
1146
|
let pathName = "{" + pathInProject + "}.";
|
|
849
1147
|
|
|
850
1148
|
// Named imports, e.g. import { ClassW } from "./complexExportModule";
|
|
1149
|
+
|
|
1150
|
+
// Start with simple import clause (without referring to the actual variable)
|
|
1151
|
+
|
|
851
1152
|
if (importDeclaration instanceof ImportDeclaration
|
|
852
1153
|
&& importElement instanceof ImportSpecifier) {
|
|
853
1154
|
importedEntityName = importElement.getName();
|
|
@@ -857,12 +1158,14 @@ export class EntityDictionary {
|
|
|
857
1158
|
}
|
|
858
1159
|
if (importedEntity === undefined) {
|
|
859
1160
|
importedEntity = new Famix.NamedEntity();
|
|
860
|
-
importedEntity.
|
|
1161
|
+
importedEntity.name = importedEntityName;
|
|
861
1162
|
if (!isInExports) {
|
|
862
|
-
importedEntity.
|
|
1163
|
+
importedEntity.isStub = true;
|
|
863
1164
|
}
|
|
1165
|
+
importedEntity.fullyQualifiedName = pathName;
|
|
864
1166
|
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
865
|
-
|
|
1167
|
+
// must add entity to repository
|
|
1168
|
+
this.famixRep.addElement(importedEntity);
|
|
866
1169
|
}
|
|
867
1170
|
}
|
|
868
1171
|
// handle import equals declarations, e.g. import myModule = require("./complexExportModule");
|
|
@@ -871,45 +1174,420 @@ export class EntityDictionary {
|
|
|
871
1174
|
importedEntityName = importDeclaration?.getName();
|
|
872
1175
|
pathName = pathName + importedEntityName;
|
|
873
1176
|
importedEntity = new Famix.StructuralEntity();
|
|
874
|
-
importedEntity.
|
|
1177
|
+
importedEntity.name = importedEntityName;
|
|
1178
|
+
initFQN(importDeclaration, importedEntity);
|
|
875
1179
|
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
876
|
-
importedEntity.
|
|
877
|
-
const anyType = this.createOrGetFamixType('any',
|
|
878
|
-
(importedEntity as Famix.StructuralEntity).
|
|
1180
|
+
importedEntity.fullyQualifiedName = pathName;
|
|
1181
|
+
const anyType = this.createOrGetFamixType('any', importDeclaration);
|
|
1182
|
+
(importedEntity as Famix.StructuralEntity).declaredType = anyType;
|
|
879
1183
|
} else { // default imports, e.g. import ClassW from "./complexExportModule";
|
|
880
1184
|
importedEntityName = importElement.getText();
|
|
881
1185
|
pathName = pathName + (isDefaultExport ? "defaultExport" : "namespaceExport");
|
|
882
1186
|
importedEntity = new Famix.NamedEntity();
|
|
883
|
-
importedEntity.
|
|
1187
|
+
importedEntity.name = importedEntityName;
|
|
1188
|
+
importedEntity.fullyQualifiedName = pathName;
|
|
884
1189
|
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
885
|
-
importedEntity.setFullyQualifiedName(pathName);
|
|
886
1190
|
}
|
|
887
|
-
|
|
888
|
-
this.famixRep.addElement(importedEntity);
|
|
1191
|
+
// I don't think it should be added to the repository if it exists already
|
|
1192
|
+
if (!isInExports) this.famixRep.addElement(importedEntity);
|
|
889
1193
|
const importerFullyQualifiedName = FQNFunctions.getFQN(importer);
|
|
890
1194
|
const fmxImporter = this.famixRep.getFamixEntityByFullyQualifiedName(importerFullyQualifiedName) as Famix.Module;
|
|
891
|
-
fmxImportClause.
|
|
892
|
-
fmxImportClause.
|
|
1195
|
+
fmxImportClause.importingEntity = fmxImporter;
|
|
1196
|
+
fmxImportClause.importedEntity = importedEntity;
|
|
893
1197
|
if (importDeclaration instanceof ImportEqualsDeclaration) {
|
|
894
|
-
fmxImportClause.
|
|
1198
|
+
fmxImportClause.moduleSpecifier = importDeclaration?.getModuleReference().getText() as string;
|
|
895
1199
|
} else {
|
|
896
|
-
fmxImportClause.
|
|
1200
|
+
fmxImportClause.moduleSpecifier = importDeclaration?.getModuleSpecifierValue() as string;
|
|
897
1201
|
}
|
|
898
1202
|
|
|
899
|
-
logger.debug(`createFamixImportClause: ${fmxImportClause.
|
|
900
|
-
Helpers.getSubTypeName(fmxImportClause.
|
|
901
|
-
|
|
902
|
-
// make an index file anchor for the import clause
|
|
903
|
-
this.makeFamixIndexFileAnchor(importDeclaration, fmxImportClause);
|
|
1203
|
+
logger.debug(`createFamixImportClause: ${fmxImportClause.importedEntity?.name} (of type ${
|
|
1204
|
+
Helpers.getSubTypeName(fmxImportClause.importedEntity)}) is imported by ${fmxImportClause.importingEntity?.name}`);
|
|
904
1205
|
|
|
905
1206
|
fmxImporter.addOutgoingImport(fmxImportClause);
|
|
906
1207
|
|
|
907
1208
|
this.famixRep.addElement(fmxImportClause);
|
|
908
1209
|
|
|
909
|
-
this.fmxElementObjectMap.set(fmxImportClause,importDeclaration);
|
|
1210
|
+
if (importDeclaration) this.fmxElementObjectMap.set(fmxImportClause, importDeclaration);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Creates a Famix Arrow Function
|
|
1215
|
+
* @param arrowExpression An Expression
|
|
1216
|
+
* @returns The Famix model of the variable
|
|
1217
|
+
*/
|
|
1218
|
+
public createFamixArrowFunction(arrowExpression: Expression, currentCC: { [key: string]: number } ): Famix.ArrowFunction | Famix.ParametricArrowFunction {
|
|
1219
|
+
|
|
1220
|
+
let fmxArrowFunction: Famix.ArrowFunction | Famix.ParametricArrowFunction;
|
|
1221
|
+
|
|
1222
|
+
const arrowFunction = arrowExpression.asKindOrThrow(SyntaxKind.ArrowFunction);
|
|
1223
|
+
|
|
1224
|
+
const isGeneric = arrowFunction.getTypeParameters().length > 0;
|
|
1225
|
+
|
|
1226
|
+
if (isGeneric) {
|
|
1227
|
+
fmxArrowFunction = new Famix.ParametricArrowFunction();
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
fmxArrowFunction = new Famix.ArrowFunction();
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// Get the parent of the arrow function (the variable declaration)
|
|
1234
|
+
const parent = arrowFunction.getParentIfKind(SyntaxKind.VariableDeclaration);
|
|
1235
|
+
let functionName = '(NO_NAME)';
|
|
1236
|
+
|
|
1237
|
+
if (parent && parent instanceof VariableDeclaration) {
|
|
1238
|
+
// Get the name of the variable
|
|
1239
|
+
functionName = parent.getName();
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (functionName) {
|
|
1243
|
+
fmxArrowFunction.name = functionName;
|
|
1244
|
+
}
|
|
1245
|
+
else {
|
|
1246
|
+
fmxArrowFunction.name = "anonymous";
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// Signature of an arrow function is (parameters) => return_type
|
|
1250
|
+
const parametersSignature = arrowFunction.getParameters().map(p => p.getText()).join(", ");
|
|
1251
|
+
const returnTypeSignature = arrowFunction.getReturnType().getText();
|
|
1252
|
+
fmxArrowFunction.signature = `(${parametersSignature}) => ${returnTypeSignature}`;
|
|
1253
|
+
fmxArrowFunction.cyclomaticComplexity = currentCC[fmxArrowFunction.name];
|
|
1254
|
+
|
|
1255
|
+
let functionTypeName = this.UNKNOWN_VALUE;
|
|
1256
|
+
try {
|
|
1257
|
+
functionTypeName = arrowFunction.getReturnType().getText().trim();
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${functionName}. Continuing...`);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
const fmxType = this.createOrGetFamixType(functionTypeName, arrowFunction as unknown as FunctionDeclaration);
|
|
1263
|
+
fmxArrowFunction.declaredType = fmxType;
|
|
1264
|
+
fmxArrowFunction.numberOfLinesOfCode = arrowFunction.getEndLineNumber() - arrowFunction.getStartLineNumber();
|
|
1265
|
+
const parameters = arrowFunction.getParameters();
|
|
1266
|
+
fmxArrowFunction.numberOfParameters = parameters.length;
|
|
1267
|
+
fmxArrowFunction.numberOfStatements = arrowFunction.getStatements().length;
|
|
1268
|
+
initFQN(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
|
|
1269
|
+
this.makeFamixIndexFileAnchor(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
|
|
1270
|
+
this.famixRep.addElement(fmxArrowFunction);
|
|
1271
|
+
this.fmxElementObjectMap.set(fmxArrowFunction,arrowFunction as unknown as TSMorphObjectType);
|
|
1272
|
+
|
|
1273
|
+
return fmxArrowFunction;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Creates a Famix concretisation
|
|
1278
|
+
* @param cls A class
|
|
1279
|
+
* @returns The Famix model of the concretisation
|
|
1280
|
+
*/
|
|
1281
|
+
public createFamixConcretisation(conEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod ,genEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod): Famix.Concretisation {
|
|
1282
|
+
|
|
1283
|
+
const fmxConcretisation : Famix.Concretisation = new Famix.Concretisation();
|
|
1284
|
+
|
|
1285
|
+
fmxConcretisation.concreteEntity = conEntity;
|
|
1286
|
+
fmxConcretisation.genericEntity = genEntity;
|
|
1287
|
+
// this.fmxElementObjectMap.set(fmxConcretisation,null);
|
|
1288
|
+
this.famixRep.addElement(fmxConcretisation);
|
|
1289
|
+
const parameterConcretisation = this.createFamixParameterConcretisation(fmxConcretisation);
|
|
1290
|
+
|
|
1291
|
+
return fmxConcretisation;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Creates a Famix concretisation
|
|
1296
|
+
* @param concretisation A FamixConcretisation
|
|
1297
|
+
* @returns The Famix model of the ParameterConcrestisation
|
|
1298
|
+
*/
|
|
1299
|
+
public createFamixParameterConcretisation(concretisation: Famix.Concretisation): Famix.ParameterConcretisation | undefined{
|
|
1300
|
+
const conClass = concretisation.concreteEntity;
|
|
1301
|
+
const genClass = concretisation.genericEntity;
|
|
1302
|
+
logger.debug(`Creating parameter concretisation between ${conClass.fullyQualifiedName} and ${genClass.fullyQualifiedName}`);
|
|
1303
|
+
const parameterConcretisations = this.famixRep._getAllEntitiesWithType("ParameterConcretisation") as Set<Famix.ParameterConcretisation>;
|
|
1304
|
+
const concreteParameters = conClass.concreteParameters;
|
|
1305
|
+
const genericParameters = genClass.genericParameters;
|
|
1306
|
+
|
|
1307
|
+
let conClassTypeParametersIterator = concreteParameters.values();
|
|
1308
|
+
let genClassTypeParametersIterator = genericParameters.values();
|
|
1309
|
+
let fmxParameterConcretisation : Famix.ParameterConcretisation | undefined = undefined;
|
|
1310
|
+
|
|
1311
|
+
for (let i = 0; i < genericParameters.size; i++) {
|
|
1312
|
+
const conClassTypeParameter = conClassTypeParametersIterator.next().value as Famix.ParameterType;
|
|
1313
|
+
const genClassTypeParameter = genClassTypeParametersIterator.next().value as Famix.ParameterType;
|
|
1314
|
+
let createParameterConcretisation : boolean = true;
|
|
1315
|
+
if(conClassTypeParameter && genClassTypeParameter && conClassTypeParameter.name != genClassTypeParameter.name){
|
|
1316
|
+
parameterConcretisations.forEach((param : Famix.ParameterConcretisation) => {
|
|
1317
|
+
if (conClassTypeParameter.name == param.concreteParameter.name && genClassTypeParameter.name == param.genericParameter.name) {
|
|
1318
|
+
createParameterConcretisation = false;
|
|
1319
|
+
fmxParameterConcretisation = param;
|
|
1320
|
+
}
|
|
1321
|
+
})
|
|
1322
|
+
if (createParameterConcretisation) {
|
|
1323
|
+
fmxParameterConcretisation = new Famix.ParameterConcretisation();
|
|
1324
|
+
fmxParameterConcretisation.genericParameter = genClassTypeParameter;
|
|
1325
|
+
fmxParameterConcretisation.concreteParameter = conClassTypeParameter;
|
|
1326
|
+
fmxParameterConcretisation.addConcretisation(concretisation);
|
|
1327
|
+
// this.fmxElementObjectMap.set(fmxParameterConcretisation,null);
|
|
1328
|
+
} else {
|
|
1329
|
+
if (!fmxParameterConcretisation) {
|
|
1330
|
+
throw new Error(`fmxParameterConcretisation was undefined for concretisation with generic parameter ${genClassTypeParameter.name} and concrete parameter ${conClassTypeParameter.name}`);
|
|
1331
|
+
}
|
|
1332
|
+
fmxParameterConcretisation.addConcretisation(concretisation);
|
|
1333
|
+
}
|
|
1334
|
+
this.famixRep.addElement(fmxParameterConcretisation);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
if (!fmxParameterConcretisation) {
|
|
1338
|
+
logger.error(`fmxParameterConcretisation was undefined for concretisation with concrete entity ${conClass.fullyQualifiedName} and generic entity ${genClass.fullyQualifiedName}`);
|
|
1339
|
+
}
|
|
1340
|
+
return fmxParameterConcretisation;
|
|
1341
|
+
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Creates a Famix concretisation between two classes or two interfaces
|
|
1346
|
+
* @param element A class or an Interface
|
|
1347
|
+
*/
|
|
1348
|
+
public createFamixConcretisationClassOrInterfaceSpecialisation(element: ClassDeclaration | InterfaceDeclaration){
|
|
1349
|
+
|
|
1350
|
+
const superEntity = element.getExtends();
|
|
1351
|
+
let superEntityArray;
|
|
1352
|
+
if (superEntity){
|
|
1353
|
+
superEntityArray = Array.isArray(superEntity) ? superEntity : [superEntity];
|
|
1354
|
+
}
|
|
1355
|
+
if (superEntityArray && superEntityArray.length > 0) {
|
|
1356
|
+
superEntityArray.forEach(entity => {
|
|
1357
|
+
let entityIsGeneric;
|
|
1358
|
+
const superEntitySymbol = entity.getExpression().getSymbolOrThrow();
|
|
1359
|
+
let superEntityDeclaration;
|
|
1360
|
+
if (superEntity instanceof ExpressionWithTypeArguments) {
|
|
1361
|
+
superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.ClassDeclaration);
|
|
1362
|
+
} else {
|
|
1363
|
+
superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.InterfaceDeclaration);
|
|
1364
|
+
}
|
|
1365
|
+
if (superEntityDeclaration) {
|
|
1366
|
+
entityIsGeneric = superEntityDeclaration.getTypeParameters().length > 0;
|
|
1367
|
+
}
|
|
1368
|
+
if (entityIsGeneric) {
|
|
1369
|
+
let EntityDeclaration;
|
|
1370
|
+
let genEntity;
|
|
1371
|
+
if (superEntity instanceof ExpressionWithTypeArguments) {
|
|
1372
|
+
EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as ClassDeclaration;
|
|
1373
|
+
genEntity = this.createOrGetFamixClass(EntityDeclaration) as Famix.ParametricClass;
|
|
1374
|
+
} else {
|
|
1375
|
+
EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
|
|
1376
|
+
genEntity = this.createOrGetFamixInterface(EntityDeclaration) as Famix.ParametricInterface;
|
|
1377
|
+
}
|
|
1378
|
+
const genParams = EntityDeclaration.getTypeParameters().map((param) => param.getText());
|
|
1379
|
+
const args = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments()
|
|
1380
|
+
const conParams = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
|
|
1381
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1382
|
+
let conEntity;
|
|
1383
|
+
conEntity = this.createOrGetFamixConcreteElement(genEntity,EntityDeclaration,args);
|
|
1384
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1385
|
+
let createConcretisation : boolean = true;
|
|
1386
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1387
|
+
if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
|
|
1388
|
+
createConcretisation = false;
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
if (createConcretisation) {
|
|
1393
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
// TODO: This function seems unfinished
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
|
|
1403
|
+
/**
|
|
1404
|
+
* Creates a Famix concretisation between a class and its instanciations
|
|
1405
|
+
* @param cls A class
|
|
1406
|
+
*/
|
|
1407
|
+
public createFamixConcretisationGenericInstantiation(cls: ClassDeclaration){
|
|
1408
|
+
|
|
1409
|
+
const isGeneric = cls.getTypeParameters().length > 0;
|
|
1410
|
+
if (isGeneric) {
|
|
1411
|
+
const instances = cls.getSourceFile().getDescendantsOfKind(ts.SyntaxKind.NewExpression)
|
|
1412
|
+
.filter(newExpr => {
|
|
1413
|
+
const expression = newExpr.getExpression();
|
|
1414
|
+
return expression.getText() === cls.getName();
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
instances.forEach(instance => {
|
|
1418
|
+
const instanceIsGeneric = instance.getTypeArguments().length > 0;
|
|
1419
|
+
if (instanceIsGeneric) {
|
|
1420
|
+
const conParams = instance.getTypeArguments().map((param) => param.getText());
|
|
1421
|
+
const genEntity = this.createOrGetFamixClass(cls) as Famix.ParametricClass;
|
|
1422
|
+
const genParams = cls.getTypeParameters().map((param) => param.getText());
|
|
1423
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1424
|
+
let conEntity;
|
|
1425
|
+
conEntity = this.createOrGetFamixConcreteElement(genEntity,cls,instance.getTypeArguments());
|
|
1426
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1427
|
+
let createConcretisation : boolean = true;
|
|
1428
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1429
|
+
if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
|
|
1430
|
+
createConcretisation = false;
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
if (createConcretisation) {
|
|
1435
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
})
|
|
1440
|
+
}
|
|
1441
|
+
// TODO: This function seems unfinished
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* Creates a Famix concretisation between a class and its instanciations
|
|
1446
|
+
* @param func A function
|
|
1447
|
+
*/
|
|
1448
|
+
public createFamixConcretisationFunctionInstantiation(element: FunctionDeclaration | MethodDeclaration){
|
|
1449
|
+
const isGeneric = element.getTypeParameters().length > 0;
|
|
1450
|
+
if (isGeneric) {
|
|
1451
|
+
const genParams = element.getTypeParameters().map(param => param.getText());
|
|
1452
|
+
const uses = element.findReferencesAsNodes();
|
|
1453
|
+
uses.forEach(usage => {
|
|
1454
|
+
let currentNode: Node | undefined = usage;
|
|
1455
|
+
|
|
1456
|
+
while (currentNode) {
|
|
1457
|
+
if (currentNode.getKind() === SyntaxKind.CallExpression) {
|
|
1458
|
+
const callExpression = currentNode.asKind(SyntaxKind.CallExpression);
|
|
1459
|
+
if (!callExpression) {
|
|
1460
|
+
throw new Error(`CallExpression not found for ${currentNode.getText()}`);
|
|
1461
|
+
}
|
|
1462
|
+
const instanceIsGeneric = callExpression.getTypeArguments().length > 0;
|
|
1463
|
+
if (instanceIsGeneric) {
|
|
1464
|
+
const args = callExpression.getTypeArguments();
|
|
1465
|
+
const conParams = callExpression.getTypeArguments().map(param => param.getText());
|
|
1466
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1467
|
+
let genElement;
|
|
1468
|
+
if(element instanceof FunctionDeclaration){
|
|
1469
|
+
genElement = this.createOrGetFamixFunction(element, {}) as Famix.ParametricFunction;
|
|
1470
|
+
} else {
|
|
1471
|
+
genElement = this.createOrGetFamixMethod(element, {}) as Famix.ParametricMethod;
|
|
1472
|
+
}
|
|
1473
|
+
let concElement;
|
|
1474
|
+
concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
|
|
1475
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1476
|
+
let createConcretisation : boolean = true;
|
|
1477
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1478
|
+
if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName){
|
|
1479
|
+
createConcretisation = false;
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
if (createConcretisation) {
|
|
1484
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1490
|
+
// Remonter à l'élément parent (utile si le nœud de référence est un enfant)
|
|
1491
|
+
currentNode = currentNode.getParent();
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* Creates a Famix concretisation between a class and an interface
|
|
1499
|
+
* @param cls A class
|
|
1500
|
+
*/
|
|
1501
|
+
public createFamixConcretisationInterfaceClass(cls: ClassDeclaration){
|
|
1502
|
+
|
|
1503
|
+
const superInterfaces = cls.getImplements();
|
|
1504
|
+
superInterfaces.forEach(interfaceType => {
|
|
1505
|
+
const interfaceIsGeneric = interfaceType.getTypeArguments().length>0;
|
|
1506
|
+
if (interfaceIsGeneric) {
|
|
1507
|
+
const interfaceDeclaration = interfaceType.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
|
|
1508
|
+
const genParams = interfaceDeclaration.getTypeParameters().map((param) => param.getText());
|
|
1509
|
+
const conParams = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
|
|
1510
|
+
const args = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments();
|
|
1511
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1512
|
+
const genInterface = this.createOrGetFamixInterface(interfaceDeclaration) as Famix.ParametricInterface;
|
|
1513
|
+
const conInterface = this.createOrGetFamixConcreteElement(genInterface,interfaceDeclaration,args);
|
|
1514
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1515
|
+
let createConcretisation : boolean = true;
|
|
1516
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1517
|
+
if (genInterface.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conInterface.fullyQualifiedName){
|
|
1518
|
+
createConcretisation = false;
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
if (createConcretisation) {
|
|
1523
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conInterface,genInterface);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* Creates a Famix concretisation between an interface and a Type
|
|
1532
|
+
* @param element A variable or a function
|
|
1533
|
+
* @param inter An interface
|
|
1534
|
+
*/
|
|
1535
|
+
public createFamixConcretisationTypeInstanciation(element: InterfaceDeclaration | ClassDeclaration){
|
|
1536
|
+
|
|
1537
|
+
const isGeneric = element.getTypeParameters().length > 0;
|
|
1538
|
+
if (isGeneric) {
|
|
1539
|
+
const genParams = element.getTypeParameters().map(param => param.getText());
|
|
1540
|
+
const uses = element.findReferencesAsNodes();
|
|
1541
|
+
uses.forEach(use => {
|
|
1542
|
+
let parentNode = use.getParent();
|
|
1543
|
+
while (parentNode) {
|
|
1544
|
+
if (parentNode.getKind() === SyntaxKind.TypeReference) {
|
|
1545
|
+
const typeReferenceNode = parentNode.asKind(SyntaxKind.TypeReference);
|
|
1546
|
+
if (!typeReferenceNode) {
|
|
1547
|
+
throw new Error(`TypeReferenceNode not found for ${parentNode.getText()}`);
|
|
1548
|
+
}
|
|
1549
|
+
const typeReferenceNodeIsGeneric = typeReferenceNode.getTypeArguments().length > 0;
|
|
1550
|
+
if (typeReferenceNodeIsGeneric) {}
|
|
1551
|
+
const args = typeReferenceNode.getTypeArguments();
|
|
1552
|
+
const conParams = typeReferenceNode.getTypeArguments().map(param => param.getText());
|
|
1553
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1554
|
+
let genElement;
|
|
1555
|
+
if(element instanceof ClassDeclaration){
|
|
1556
|
+
genElement = this.createOrGetFamixClass(element) as Famix.ParametricClass;
|
|
1557
|
+
} else {
|
|
1558
|
+
genElement = this.createOrGetFamixInterface(element) as Famix.ParametricInterface;
|
|
1559
|
+
}
|
|
1560
|
+
let concElement;
|
|
1561
|
+
concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
|
|
1562
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1563
|
+
let createConcretisation : boolean = true;
|
|
1564
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1565
|
+
if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName){
|
|
1566
|
+
createConcretisation = false;
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
if (createConcretisation) {
|
|
1571
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
break;
|
|
1575
|
+
}
|
|
1576
|
+
parentNode = parentNode.getParent();
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
910
1580
|
}
|
|
911
1581
|
|
|
912
1582
|
public convertToRelativePath(absolutePath: string, absolutePathProject: string) {
|
|
913
1583
|
return absolutePath.replace(absolutePathProject, "").slice(1);
|
|
914
1584
|
}
|
|
915
|
-
}
|
|
1585
|
+
}
|
|
1586
|
+
function initFQN(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity) {
|
|
1587
|
+
if (!(sourceElement instanceof CommentRange)) {
|
|
1588
|
+
const fqn = FQNFunctions.getFQN(sourceElement);
|
|
1589
|
+
logger.debug("Setting fully qualified name for " + famixElement.getJSON() + " to " + fqn);
|
|
1590
|
+
(famixElement as Famix.NamedEntity).fullyQualifiedName = fqn;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|