@abaplint/transpiler-cli 2.14.11 → 2.14.13

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/README.md CHANGED
@@ -98,6 +98,29 @@ The transpiler library retains flat output unless its caller supplies an output
98
98
  folder map as the third argument to `Transpiler.run()`. This layout is enabled
99
99
  automatically by the CLI.
100
100
 
101
+ ## Pinning a library
102
+
103
+ A library given by `url` is cloned when the CLI runs, so without more the
104
+ default branch of that moment is used and the same project commit can build
105
+ differently from one day to the next. Set `libs[].ref` to a commit SHA, a tag
106
+ or a branch to make the build reproducible:
107
+
108
+ ```json
109
+ {
110
+ "libs": [
111
+ {"url": "https://github.com/open-abap/open-abap-core", "ref": "239b817e6fd05e62854fed25663f002c60dda07c"},
112
+ {"url": "https://github.com/example/utilities", "ref": "v1.2.0"}
113
+ ]
114
+ }
115
+ ```
116
+
117
+ A branch or tag is cloned shallow. A commit is fetched shallow by name; when
118
+ the server does not serve that, or the SHA is abbreviated, the full history is
119
+ fetched and the commit checked out from it. The CLI prints the commit of every
120
+ library it clones, and of every `folder` library that is a git checkout, as
121
+ `commit: <sha>` next to the file count, so the commits of a build can be
122
+ recorded.
123
+
101
124
  ## Duplicate dependencies
102
125
 
103
126
  By default, two libraries defining the same ABAP object (type and name) cause
package/build/bundle.js CHANGED
@@ -257,14 +257,101 @@ exports.FileOperations = FileOperations;
257
257
  /*!****************************!*\
258
258
  !*** ./build/git_clone.js ***!
259
259
  \****************************/
260
- (__unused_webpack_module, exports) {
260
+ (__unused_webpack_module, exports, __webpack_require__) {
261
261
 
262
262
  "use strict";
263
263
 
264
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
265
+ if (k2 === undefined) k2 = k;
266
+ var desc = Object.getOwnPropertyDescriptor(m, k);
267
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
268
+ desc = { enumerable: true, get: function() { return m[k]; } };
269
+ }
270
+ Object.defineProperty(o, k2, desc);
271
+ }) : (function(o, m, k, k2) {
272
+ if (k2 === undefined) k2 = k;
273
+ o[k2] = m[k];
274
+ }));
275
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
276
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
277
+ }) : function(o, v) {
278
+ o["default"] = v;
279
+ });
280
+ var __importStar = (this && this.__importStar) || (function () {
281
+ var ownKeys = function(o) {
282
+ ownKeys = Object.getOwnPropertyNames || function (o) {
283
+ var ar = [];
284
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
285
+ return ar;
286
+ };
287
+ return ownKeys(o);
288
+ };
289
+ return function (mod) {
290
+ if (mod && mod.__esModule) return mod;
291
+ var result = {};
292
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
293
+ __setModuleDefault(result, mod);
294
+ return result;
295
+ };
296
+ })();
264
297
  Object.defineProperty(exports, "__esModule", ({ value: true }));
298
+ exports.isCommitSha = isCommitSha;
265
299
  exports.buildGitCloneArguments = buildGitCloneArguments;
266
- function buildGitCloneArguments(url) {
267
- return ["clone", "--quiet", "--depth", "1", "--", url, "."];
300
+ exports.cloneRepository = cloneRepository;
301
+ exports.resolveHead = resolveHead;
302
+ const childProcess = __importStar(__webpack_require__(/*! child_process */ "child_process"));
303
+ /** a full or abbreviated commit SHA, as opposed to a branch or tag name */
304
+ function isCommitSha(ref) {
305
+ return /^[0-9a-f]{7,40}$/i.test(ref);
306
+ }
307
+ /** shallow clone of the default branch, or of the branch or tag `ref` */
308
+ function buildGitCloneArguments(url, ref) {
309
+ const branch = ref === undefined || ref === "" ? [] : ["--branch", ref];
310
+ return ["clone", "--quiet", "--depth", "1", ...branch, "--", url, "."];
311
+ }
312
+ function git(args, cwd, stderr = "inherit") {
313
+ childProcess.execFileSync("git", args, { cwd, stdio: ["ignore", "inherit", stderr] });
314
+ }
315
+ /** Clones `url` into the empty directory `dir` and returns the commit checked out.
316
+ *
317
+ * Without `ref` this is a shallow clone of the default branch. A branch or tag
318
+ * is cloned shallow with --branch. A commit SHA is fetched shallow by name,
319
+ * which GitHub and every server with uploadpack.allowReachableSHA1InWant
320
+ * serve; when the server refuses that, or the SHA is abbreviated, the full
321
+ * history is fetched and the commit checked out from it. */
322
+ function cloneRepository(url, dir, ref) {
323
+ if (ref === undefined || ref === "") {
324
+ git(buildGitCloneArguments(url), dir);
325
+ }
326
+ else if (ref.startsWith("-")) {
327
+ throw new Error("Invalid lib ref: " + JSON.stringify(ref));
328
+ }
329
+ else if (isCommitSha(ref)) {
330
+ git(["init", "--quiet"], dir);
331
+ git(["remote", "add", "origin", "--", url], dir);
332
+ try {
333
+ git(["fetch", "--quiet", "--depth", "1", "origin", ref], dir, "pipe");
334
+ git(["checkout", "--quiet", "--detach", "FETCH_HEAD"], dir);
335
+ }
336
+ catch {
337
+ console.log("\tshallow fetch of " + ref + " not served, fetching the full history");
338
+ git(["fetch", "--quiet", "--tags", "origin"], dir);
339
+ git(["checkout", "--quiet", "--detach", ref], dir);
340
+ }
341
+ }
342
+ else {
343
+ git(buildGitCloneArguments(url, ref), dir);
344
+ }
345
+ return resolveHead(dir);
346
+ }
347
+ /** the commit checked out in `dir`, undefined when it is not a git checkout */
348
+ function resolveHead(dir) {
349
+ try {
350
+ return childProcess.execFileSync("git", ["rev-parse", "HEAD"], { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
351
+ }
352
+ catch {
353
+ return undefined;
354
+ }
268
355
  }
269
356
  //# sourceMappingURL=git_clone.js.map
270
357
 
@@ -485,7 +572,6 @@ exports.loadLibraries = loadLibraries;
485
572
  exports.libraryRegistry = libraryRegistry;
486
573
  const fs = __importStar(__webpack_require__(/*! fs */ "fs"));
487
574
  const path = __importStar(__webpack_require__(/*! path */ "path"));
488
- const childProcess = __importStar(__webpack_require__(/*! child_process */ "child_process"));
489
575
  const os = __importStar(__webpack_require__(/*! os */ "os"));
490
576
  const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js"));
491
577
  const file_operations_1 = __webpack_require__(/*! ./file_operations */ "./build/file_operations.js");
@@ -533,13 +619,14 @@ async function loadLibraries(config) {
533
619
  ? "Library must define a non-empty url or an existing folder"
534
620
  : "Library folder not found: " + folder);
535
621
  }
536
- console.log("Clone: " + lib.url);
622
+ console.log("Clone: " + lib.url + (lib.ref ? " at " + lib.ref : ""));
537
623
  dir = fs.mkdtempSync(path.join(os.tmpdir(), "abap_transpile-"));
538
624
  cleanupFolder = true;
539
625
  }
540
626
  try {
541
- if (cleanupFolder) {
542
- childProcess.execFileSync("git", (0, git_clone_1.buildGitCloneArguments)(lib.url), { cwd: dir, stdio: "inherit" });
627
+ const commit = cleanupFolder ? (0, git_clone_1.cloneRepository)(lib.url, dir, lib.ref) : (0, git_clone_1.resolveHead)(dir);
628
+ if (commit !== undefined) {
629
+ console.log("\tcommit: " + commit);
543
630
  }
544
631
  const patterns = typeof lib.files === "string" && lib.files !== "" ? [lib.files]
545
632
  : Array.isArray(lib.files) ? lib.files : ["/src/**"];
@@ -6282,10 +6369,15 @@ class FormParamType extends combi_1.Expression {
6282
6369
  getRunnable() {
6283
6370
  const def = (0, combi_1.seq)("DEFAULT", (0, combi_1.alt)(_1.Constant, _1.FieldChain));
6284
6371
  const table = (0, combi_1.seq)((0, combi_1.alt)("STANDARD", "HASHED", "INDEX", "SORTED", "ANY"), "TABLE");
6285
- const tabseq = (0, combi_1.seq)(table, (0, combi_1.optPrio)((0, combi_1.seq)("OF", _1.TypeName)));
6286
- const ret = (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.TypeName, (0, combi_1.opt)(def));
6287
- const like = (0, combi_1.seq)("LIKE", (0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.FieldChain);
6288
- return (0, combi_1.alt)((0, combi_1.seq)("TYPE", (0, combi_1.altPrio)(tabseq, (0, combi_1.ver)(version_1.Release.v916, (0, combi_1.seq)("ANY", "STRUCTURE")), ret)), like);
6372
+ // the following are not valid FORM typings, fail instead of reading the
6373
+ // remaining words as additional parameters
6374
+ const inline = (0, combi_1.seq)((0, combi_1.alt)("TABLE", "RANGE"), "OF", (0, combi_1.failCombinator)());
6375
+ const tableKey = (0, combi_1.seq)("WITH", (0, combi_1.failCombinator)());
6376
+ const lengthDecimals = (0, combi_1.seq)((0, combi_1.alt)("LENGTH", "DECIMALS"), _1.Integer, (0, combi_1.failCombinator)());
6377
+ const tabseq = (0, combi_1.seq)(table, (0, combi_1.optPrio)((0, combi_1.seq)("OF", (0, combi_1.altPrio)((0, combi_1.seq)("REF TO", (0, combi_1.failCombinator)()), _1.TypeName))), (0, combi_1.optPrio)(tableKey));
6378
+ const ret = (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.TypeName, (0, combi_1.optPrio)(lengthDecimals), (0, combi_1.opt)(def));
6379
+ const like = (0, combi_1.seq)("LIKE", (0, combi_1.altPrio)(inline, (0, combi_1.seq)(table, (0, combi_1.failCombinator)()), (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.FieldChain)));
6380
+ return (0, combi_1.alt)((0, combi_1.seq)("TYPE", (0, combi_1.altPrio)(tabseq, (0, combi_1.ver)(version_1.Release.v916, (0, combi_1.seq)("ANY", "STRUCTURE")), inline, ret)), like);
6289
6381
  }
6290
6382
  }
6291
6383
  exports.FormParamType = FormParamType;
@@ -29558,6 +29650,15 @@ class TypeUtils {
29558
29650
  }
29559
29651
  return this.isAssignable(source, target);
29560
29652
  }
29653
+ // character-like arguments of built-in functions must be elementary, flat character-like structures are not allowed
29654
+ isAssignableBuiltin(source, target, node) {
29655
+ if (target instanceof basic_1.CLikeType
29656
+ && (source instanceof basic_1.StructureType
29657
+ || (source instanceof basic_1.TableType && source.isWithHeader() && source.getRowType() instanceof basic_1.StructureType))) {
29658
+ return false;
29659
+ }
29660
+ return this.isAssignableStrict(source, target, node);
29661
+ }
29561
29662
  // Typing with the classic STRUCTURE addition, ie. "FORM foo USING bar STRUCTURE zkey",
29562
29663
  // is not a compatibility check: ABAP only checks the fragment view up to the end of the
29563
29664
  // formal structure, so the actual parameter is allowed to be longer. For TABLES parameters
@@ -29854,6 +29955,7 @@ const expressions_1 = __webpack_require__(/*! ../2_statements/expressions */ "./
29854
29955
  const _builtin_1 = __webpack_require__(/*! ./_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
29855
29956
  const position_1 = __webpack_require__(/*! ../../position */ "./node_modules/@abaplint/core/build/src/position.js");
29856
29957
  const _syntax_input_1 = __webpack_require__(/*! ./_syntax_input */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_syntax_input.js");
29958
+ const version_1 = __webpack_require__(/*! ../../version */ "./node_modules/@abaplint/core/build/src/version.js");
29857
29959
  class BasicTypes {
29858
29960
  constructor(input) {
29859
29961
  this.input = input;
@@ -30079,7 +30181,14 @@ class BasicTypes {
30079
30181
  }
30080
30182
  const chainText = typeName.concatTokens().toUpperCase();
30081
30183
  const f = this.input.scope.getDDIC().lookupBuiltinType(chainText, length, decimals, qualifiedName);
30082
- if (f !== undefined) {
30184
+ if (f instanceof Types.Integer8Type
30185
+ && !(0, version_1.releaseAtLeast)(this.input.scope.getRelease(), version_1.Release.v750)
30186
+ && !this.input.scope.getOpenABAP()) {
30187
+ const message = "Type INT8 requires 7.50";
30188
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, typeName.getFirstToken(), message));
30189
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
30190
+ }
30191
+ else if (f !== undefined) {
30083
30192
  return f;
30084
30193
  }
30085
30194
  const typ = this.input.scope.findType(chainText);
@@ -34839,6 +34948,7 @@ const _syntax_input_1 = __webpack_require__(/*! ../_syntax_input */ "./node_modu
34839
34948
  const constant_1 = __webpack_require__(/*! ./constant */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/constant.js");
34840
34949
  const _check_offset_length_1 = __webpack_require__(/*! ./_check_offset_length */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_offset_length.js");
34841
34950
  const _check_text_symbol_1 = __webpack_require__(/*! ./_check_text_symbol */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_text_symbol.js");
34951
+ const _builtin_1 = __webpack_require__(/*! ../_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
34842
34952
  class MethodCallParam {
34843
34953
  /** returns the type of the unnamed argument, if any */
34844
34954
  static runSyntax(node, input, method) {
@@ -34915,7 +35025,7 @@ class MethodCallParam {
34915
35025
  else if ((0, _check_text_symbol_1.checkTextSymbol)(child, targetType, input)) {
34916
35026
  return;
34917
35027
  }
34918
- else if (new _type_utils_1.TypeUtils(input.scope).isAssignableStrict(sourceType, targetType, child) === false) {
35028
+ else if (this.isAssignable(sourceType, targetType, child, method, input) === false) {
34919
35029
  const message = "Method parameter type not compatible";
34920
35030
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
34921
35031
  return;
@@ -34935,6 +35045,12 @@ class MethodCallParam {
34935
35045
  }
34936
35046
  return undefined;
34937
35047
  }
35048
+ static isAssignable(sourceType, targetType, child, method, input) {
35049
+ const typeUtils = new _type_utils_1.TypeUtils(input.scope);
35050
+ return method instanceof _builtin_1.BuiltInMethod
35051
+ ? typeUtils.isAssignableBuiltin(sourceType, targetType, child)
35052
+ : typeUtils.isAssignableStrict(sourceType, targetType, child);
35053
+ }
34938
35054
  }
34939
35055
  exports.MethodCallParam = MethodCallParam;
34940
35056
  //# sourceMappingURL=method_call_param.js.map
@@ -35172,6 +35288,7 @@ const field_chain_1 = __webpack_require__(/*! ./field_chain */ "./node_modules/@
35172
35288
  const _reference_1 = __webpack_require__(/*! ../_reference */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_reference.js");
35173
35289
  const _check_offset_length_1 = __webpack_require__(/*! ./_check_offset_length */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_offset_length.js");
35174
35290
  const _check_text_symbol_1 = __webpack_require__(/*! ./_check_text_symbol */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_text_symbol.js");
35291
+ const _builtin_1 = __webpack_require__(/*! ../_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
35175
35292
  function normalizeParameterName(name) {
35176
35293
  return name.replace(/^!/, "").toUpperCase();
35177
35294
  }
@@ -35319,6 +35436,10 @@ class MethodParameters {
35319
35436
  if (this.requiredParameters === undefined) {
35320
35437
  this.requiredParameters = new Set(method.getParameters().getRequiredParameters().map(i => i.getName().toUpperCase()));
35321
35438
  }
35439
+ const typeUtils = new _type_utils_1.TypeUtils(input.scope);
35440
+ const assignable = method instanceof _builtin_1.BuiltInMethod
35441
+ ? typeUtils.isAssignableBuiltin.bind(typeUtils)
35442
+ : typeUtils.isAssignableStrict.bind(typeUtils);
35322
35443
  for (const item of items) {
35323
35444
  const parameter = allImporting.find(p => p.getName().toUpperCase() === item.name);
35324
35445
  if (parameter === undefined) {
@@ -35329,7 +35450,7 @@ class MethodParameters {
35329
35450
  else if ((0, _check_text_symbol_1.checkTextSymbol)(item.source, parameter.getType(), input)) {
35330
35451
  return;
35331
35452
  }
35332
- else if (new _type_utils_1.TypeUtils(input.scope).isAssignableStrict(item.sourceType, parameter.getType(), item.source) === false) {
35453
+ else if (assignable(item.sourceType, parameter.getType(), item.source) === false) {
35333
35454
  const message = "Method parameter type not compatible, " + item.name;
35334
35455
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
35335
35456
  return;
@@ -44563,6 +44684,10 @@ class InsertInternal {
44563
44684
  if (sourceType instanceof basic_1.IntegerType && targetType instanceof basic_1.Integer8Type) {
44564
44685
  error = true;
44565
44686
  }
44687
+ else if (targetType instanceof basic_1.TableType && targetType.isWithHeader() && sourceType instanceof basic_1.TableType) {
44688
+ // LINES OF into a table with header line, compare the row types
44689
+ error = new _type_utils_1.TypeUtils(input.scope).isAssignable(sourceType.getRowType(), targetType.getRowType()) === false;
44690
+ }
44566
44691
  else if (new _type_utils_1.TypeUtils(input.scope).isAssignable(sourceType, targetType) === false) {
44567
44692
  error = true;
44568
44693
  }
@@ -57728,7 +57853,7 @@ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@
57728
57853
  const ddic_1 = __webpack_require__(/*! ../ddic */ "./node_modules/@abaplint/core/build/src/ddic.js");
57729
57854
  class CDSDetermineTypes {
57730
57855
  parseType(reg, parsedData, ddlsName) {
57731
- var _a;
57856
+ var _a, _b;
57732
57857
  const ddic = new ddic_1.DDIC(reg);
57733
57858
  if ((parsedData === null || parsedData === void 0 ? void 0 : parsedData.fields.length) === 0) {
57734
57859
  return basic_1.VoidType.get("DDLS:todo");
@@ -57807,13 +57932,29 @@ class CDSDetermineTypes {
57807
57932
  else {
57808
57933
  components.push({
57809
57934
  name: f.name,
57810
- type: basic_1.VoidType.get("DDLS:fieldname:" + ddlsName),
57935
+ type: (_b = this.findInSources(ddic, parsedData, f.nameInSource)) !== null && _b !== void 0 ? _b : basic_1.VoidType.get("DDLS:fieldname:" + ddlsName),
57811
57936
  });
57812
57937
  }
57813
57938
  }
57814
57939
  return new basic_1.StructureType(components, parsedData.definitionName, parsedData.definitionName, parsedData.description);
57815
57940
  }
57816
57941
  }
57942
+ /** unprefixed field, take the type from the first source containing it, void or unknown sources are skipped */
57943
+ findInSources(ddic, parsedData, nameInSource) {
57944
+ if (nameInSource === undefined) {
57945
+ return undefined;
57946
+ }
57947
+ for (const source of parsedData.sources) {
57948
+ const lookup = ddic.lookupTableOrView(source.name);
57949
+ if (lookup.type instanceof basic_1.StructureType) {
57950
+ const type = lookup.type.getComponentByName(nameInSource);
57951
+ if (type) {
57952
+ return type;
57953
+ }
57954
+ }
57955
+ }
57956
+ return undefined;
57957
+ }
57817
57958
  }
57818
57959
  exports.CDSDetermineTypes = CDSDetermineTypes;
57819
57960
  //# sourceMappingURL=cds_determine_types.js.map
@@ -61376,8 +61517,8 @@ var __importStar = (this && this.__importStar) || (function () {
61376
61517
  };
61377
61518
  })();
61378
61519
  Object.defineProperty(exports, "__esModule", ({ value: true }));
61379
- exports.ABAPObject = exports.Tokens = exports.ExpressionsCDS = exports.CDSParser = exports.LanguageServerTypes = exports.DDLKind = exports.DDLParser = exports.AbstractFile = exports.ABAPFile = exports.MemoryFile = exports.Edits = exports.ReferenceType = exports.NativeSQL = exports.MacroContent = exports.MacroCall = exports.SpaghettiScopeNode = exports.Token = exports.ScopeType = exports.BasicTypes = exports.TypedIdentifier = exports.AbstractType = exports.VirtualPosition = exports.Comment = exports.Unknown = exports.Empty = exports.Identifier = exports.Nodes = exports.Types = exports.Expressions = exports.Statements = exports.Structures = exports.SkipLogic = exports.Objects = exports.ArtifactsRules = exports.ArtifactsObjects = exports.ArtifactsABAP = exports.BuiltIn = exports.FormLengthStats = exports.FunctionLengthStats = exports.MethodLengthStats = exports.LanguageServer = exports.Registry = exports.CyclomaticComplexityStats = exports.versionToABAPRelease = exports.releaseAtLeast = exports.Release = exports.LanguageVersion = exports.Version = exports.Config = exports.Issue = void 0;
61380
- exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = exports.CurrentScope = exports.RulesRunner = exports.SpaghettiScope = exports.SyntaxLogic = void 0;
61520
+ exports.Tokens = exports.ABAPParser = exports.ExpressionsCDS = exports.CDSParser = exports.LanguageServerTypes = exports.DDLKind = exports.DDLParser = exports.AbstractFile = exports.ABAPFile = exports.MemoryFile = exports.Edits = exports.ReferenceType = exports.NativeSQL = exports.MacroContent = exports.MacroCall = exports.SpaghettiScopeNode = exports.Token = exports.ScopeType = exports.BasicTypes = exports.TypedIdentifier = exports.AbstractType = exports.VirtualPosition = exports.Comment = exports.Unknown = exports.Empty = exports.Identifier = exports.Nodes = exports.Types = exports.Expressions = exports.Statements = exports.Structures = exports.SkipLogic = exports.Objects = exports.ArtifactsRules = exports.ArtifactsObjects = exports.ArtifactsABAP = exports.BuiltIn = exports.FormLengthStats = exports.FunctionLengthStats = exports.MethodLengthStats = exports.LanguageServer = exports.Registry = exports.CyclomaticComplexityStats = exports.versionToABAPRelease = exports.releaseAtLeast = exports.Release = exports.LanguageVersion = exports.Version = exports.Config = exports.Issue = void 0;
61521
+ exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = exports.CurrentScope = exports.RulesRunner = exports.SpaghettiScope = exports.SyntaxLogic = exports.ABAPObject = void 0;
61381
61522
  const issue_1 = __webpack_require__(/*! ./issue */ "./node_modules/@abaplint/core/build/src/issue.js");
61382
61523
  Object.defineProperty(exports, "Issue", { enumerable: true, get: function () { return issue_1.Issue; } });
61383
61524
  const config_1 = __webpack_require__(/*! ./config */ "./node_modules/@abaplint/core/build/src/config.js");
@@ -61474,6 +61615,8 @@ Object.defineProperty(exports, "DDLParser", { enumerable: true, get: function ()
61474
61615
  Object.defineProperty(exports, "DDLKind", { enumerable: true, get: function () { return ddl_parser_1.DDLKind; } });
61475
61616
  const cds_parser_1 = __webpack_require__(/*! ./cds/cds_parser */ "./node_modules/@abaplint/core/build/src/cds/cds_parser.js");
61476
61617
  Object.defineProperty(exports, "CDSParser", { enumerable: true, get: function () { return cds_parser_1.CDSParser; } });
61618
+ const abap_parser_1 = __webpack_require__(/*! ./abap/abap_parser */ "./node_modules/@abaplint/core/build/src/abap/abap_parser.js");
61619
+ Object.defineProperty(exports, "ABAPParser", { enumerable: true, get: function () { return abap_parser_1.ABAPParser; } });
61477
61620
  const rules_runner_1 = __webpack_require__(/*! ./rules_runner */ "./node_modules/@abaplint/core/build/src/rules_runner.js");
61478
61621
  Object.defineProperty(exports, "RulesRunner", { enumerable: true, get: function () { return rules_runner_1.RulesRunner; } });
61479
61622
  const _irule_1 = __webpack_require__(/*! ./rules/_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
@@ -66526,12 +66669,16 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
66526
66669
  let found = (_a = e.findDirectExpression(expressions_1.CDSAs)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(expressions_1.CDSName);
66527
66670
  const list = (_b = e.findDirectExpression(expressions_1.CDSPrefixedName)) === null || _b === void 0 ? void 0 : _b.findAllExpressions(expressions_1.CDSName);
66528
66671
  if (list !== undefined) {
66529
- const redirected = list.length > 1 && e.concatTokens().toUpperCase().includes(" REDIRECTED TO ");
66672
+ const redirected = e.concatTokens().toUpperCase().includes(" REDIRECTED TO ");
66530
66673
  if (list.length > 1 && redirected === false) {
66531
66674
  // "prefix.field" is looked up as "field" in "prefix", also when aliased via "as"
66532
66675
  prefix = list[0].concatTokens();
66533
66676
  nameInSource = list[list.length - 1].concatTokens();
66534
66677
  }
66678
+ else if (list.length === 1 && redirected === false) {
66679
+ // unprefixed "field" is looked up in the sources, also when aliased via "as"
66680
+ nameInSource = list[0].concatTokens();
66681
+ }
66535
66682
  if (found === undefined) {
66536
66683
  found = redirected === true ? list[0] : list[list.length - 1];
66537
66684
  }
@@ -73714,7 +73861,7 @@ class Registry {
73714
73861
  }
73715
73862
  static abaplintVersion() {
73716
73863
  // magic, see build script "version.js"
73717
- return "2.120.86";
73864
+ return "2.120.89";
73718
73865
  }
73719
73866
  getDDICReferences() {
73720
73867
  return this.ddicReferences;
@@ -77371,6 +77518,7 @@ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/ex
77371
77518
  const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
77372
77519
  const Structures = __importStar(__webpack_require__(/*! ../abap/3_structures/structures */ "./node_modules/@abaplint/core/build/src/abap/3_structures/structures/index.js"));
77373
77520
  const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
77521
+ const tokens_1 = __webpack_require__(/*! ../abap/1_lexer/tokens */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/tokens/index.js");
77374
77522
  class AlignTypeExpressionsConf extends _basic_rule_config_1.BasicRuleConfig {
77375
77523
  constructor() {
77376
77524
  super(...arguments);
@@ -77538,12 +77686,18 @@ ENDINTERFACE.`,
77538
77686
  let column = 0;
77539
77687
  const st = t.findDirectStatements(Statements.Type);
77540
77688
  for (const s of st) {
77541
- const name = s.getChildren()[1];
77689
+ const children = s.getChildren();
77690
+ const length = s.findDirectExpression(Expressions.ConstantFieldLength);
77691
+ const nameEnd = (length !== null && length !== void 0 ? length : children[1]).getLastToken().getEnd();
77692
+ const after = children[length ? 3 : 2];
77693
+ if (after === undefined || after.getFirstToken() instanceof tokens_1.Punctuation) {
77694
+ continue; // no TYPE clause, eg. "foo(1)," with implicit type
77695
+ }
77542
77696
  fields.push({
77543
- nameEnd: name.getLastToken().getEnd(),
77544
- after: s.getChildren()[2].getFirstToken().getStart()
77697
+ nameEnd: nameEnd,
77698
+ after: after.getFirstToken().getStart()
77545
77699
  });
77546
- column = Math.max(column, name.getFirstToken().getEnd().getCol() + 1);
77700
+ column = Math.max(column, nameEnd.getCol() + 1);
77547
77701
  }
77548
77702
  issues.push(...this.check(fields, column, file));
77549
77703
  }
@@ -85489,6 +85643,29 @@ Make sure to test the downported code, it might not always be completely correct
85489
85643
  }
85490
85644
  }
85491
85645
  }
85646
+ // a chained call with EXPORTING/IMPORTING/CHANGING/RECEIVING/EXCEPTIONS parses in the low
85647
+ // version, but 702 reads the last call of a chain as a functional call,
85648
+ // "Unexpected word CHANGING in functional method call"
85649
+ if (ret.length === 0) {
85650
+ for (let i = 0; i < lowStatements.length; i++) {
85651
+ const issue = this.outlineChainedCallParameters(lowStatements[i], highStatements[i], lowFile, highSyntax);
85652
+ if (issue) {
85653
+ ret.push(issue);
85654
+ break;
85655
+ }
85656
+ }
85657
+ }
85658
+ // INSERT with a 702 built-in function as the operand parses in the low version, but 702
85659
+ // reads the function as a method call, "Method SUBSTRING does not exist"
85660
+ if (ret.length === 0) {
85661
+ for (let i = 0; i < lowStatements.length; i++) {
85662
+ const issue = this.outlineInsertBuiltin(lowStatements[i], highStatements[i], lowFile, highSyntax);
85663
+ if (issue) {
85664
+ ret.push(issue);
85665
+ break;
85666
+ }
85667
+ }
85668
+ }
85492
85669
  // INTO after WHERE parses as a valid SELECT in the low version, but is only allowed in
85493
85670
  // strict SQL syntax, so move it also when the statement is no longer Unknown
85494
85671
  if (ret.length === 0) {
@@ -86321,6 +86498,40 @@ ${indentation}`);
86321
86498
  }
86322
86499
  return undefined;
86323
86500
  }
86501
+ /** INSERT substring( ... ) INTO TABLE tab, the operand is outlined into a variable */
86502
+ outlineInsertBuiltin(low, high, lowFile, highSyntax) {
86503
+ var _a;
86504
+ if (!(high.get() instanceof Statements.InsertInternal)) {
86505
+ return undefined;
86506
+ }
86507
+ else if (this.lowReg.getConfig().isOpenABAP()) {
86508
+ return undefined;
86509
+ }
86510
+ else if (low.getFirstToken().getStart() instanceof virtual_position_1.VirtualPosition) {
86511
+ return undefined;
86512
+ }
86513
+ // the whole operand is the call, a longer expression is outlined by replaceInsertExpression
86514
+ const source = high.getChildren()[1];
86515
+ if (!(source instanceof nodes_1.ExpressionNode)
86516
+ || !(source.get() instanceof Expressions.SimpleSource4 || source.get() instanceof Expressions.Source)
86517
+ || source.getChildren().length !== 1
86518
+ || !(((_a = source.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.MethodCallChain)) {
86519
+ return undefined;
86520
+ }
86521
+ const type = this.builtinReturnType(source, lowFile, highSyntax);
86522
+ if (type === undefined) {
86523
+ return undefined;
86524
+ }
86525
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
86526
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
86527
+ const code = `DATA ${uniqueName} TYPE ${type}.\n` +
86528
+ indentation + `${uniqueName} = ${source.concatTokens()}.\n` +
86529
+ indentation;
86530
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), code);
86531
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, source.getFirstToken().getStart(), source.getLastToken().getEnd(), uniqueName);
86532
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
86533
+ return issue_1.Issue.atToken(lowFile, source.getFirstToken(), "Outline built-in function in INSERT", this.getMetadata().key, this.conf.severity, fix);
86534
+ }
86324
86535
  /** the ABAP type returned by the built-in function this Source starts with, or undefined
86325
86536
  * if it does not start with one, or with one that predates 702 */
86326
86537
  builtinReturnType(source, lowFile, highSyntax) {
@@ -88025,6 +88236,67 @@ ${indentation} output = ${uniqueName}.\n`;
88025
88236
  }
88026
88237
  return undefined;
88027
88238
  }
88239
+ /** a( )->b( CHANGING c = d ), the object the last method is called on is outlined into a
88240
+ * variable typed via the RETURNING parameter of the method before it */
88241
+ outlineChainedCallParameters(low, high, lowFile, highSyntax) {
88242
+ var _a, _b, _c, _d;
88243
+ if (!(high.get() instanceof Statements.Call)) {
88244
+ return undefined;
88245
+ }
88246
+ else if (this.lowReg.getConfig().isOpenABAP()) {
88247
+ return undefined;
88248
+ }
88249
+ else if (low.getFirstToken().getStart() instanceof virtual_position_1.VirtualPosition) {
88250
+ return undefined;
88251
+ }
88252
+ const chain = high.findDirectExpression(Expressions.MethodCallChain);
88253
+ const children = (chain === null || chain === void 0 ? void 0 : chain.getChildren()) || [];
88254
+ const last = children[children.length - 1];
88255
+ const arrow = children[children.length - 2];
88256
+ const previous = children[children.length - 3];
88257
+ if (chain === undefined
88258
+ || !(last instanceof nodes_1.ExpressionNode)
88259
+ || !(last.get() instanceof Expressions.MethodCall)
88260
+ || (arrow === null || arrow === void 0 ? void 0 : arrow.getFirstToken().getStr()) !== "->"
88261
+ || !(previous instanceof nodes_1.ExpressionNode)
88262
+ || !(previous.get() instanceof Expressions.MethodCall)) {
88263
+ return undefined;
88264
+ }
88265
+ const parameters = (_a = last.findDirectExpression(Expressions.MethodCallParam)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.MethodParameters);
88266
+ if (parameters === undefined) {
88267
+ return undefined;
88268
+ }
88269
+ // the type of the object, from the method that returns it
88270
+ const methodToken = (_b = previous.findDirectExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.getFirstToken();
88271
+ if (methodToken === undefined) {
88272
+ return undefined;
88273
+ }
88274
+ const spag = highSyntax.spaghetti.lookupPosition(methodToken.getStart(), lowFile.getFilename());
88275
+ let type = undefined;
88276
+ for (const r of (spag === null || spag === void 0 ? void 0 : spag.getData().references) || []) {
88277
+ if (r.referenceType === _reference_1.ReferenceType.MethodReference
88278
+ && r.position.getStart().equals(methodToken.getStart())) {
88279
+ const returning = (_d = (_c = r.resolved) === null || _c === void 0 ? void 0 : _c.getParameters().getReturning()) === null || _d === void 0 ? void 0 : _d.getType();
88280
+ if (returning instanceof basic_1.ObjectReferenceType) {
88281
+ type = returning.toABAP();
88282
+ }
88283
+ break;
88284
+ }
88285
+ }
88286
+ if (type === undefined) {
88287
+ return undefined;
88288
+ }
88289
+ const object = children.slice(0, children.length - 2).map(c => c.concatTokens()).join("");
88290
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
88291
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
88292
+ const code = `DATA ${uniqueName} TYPE ${type}.\n` +
88293
+ indentation + `${uniqueName} = ${object}.\n` +
88294
+ indentation;
88295
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), code);
88296
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, chain.getFirstToken().getStart(), previous.getLastToken().getEnd(), uniqueName);
88297
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
88298
+ return issue_1.Issue.atToken(lowFile, chain.getFirstToken(), "Outline object of chained call with parameter keywords", this.getMetadata().key, this.conf.severity, fix);
88299
+ }
88028
88300
  /** if the source expression is the complete source of an INSERT or APPEND statement, then the
88029
88301
  * outlined variable must be typed via the row type of the table, returns the table name */
88030
88302
  findRowTypeOf(high, source) {
@@ -102851,7 +103123,7 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102851
103123
  continue;
102852
103124
  }
102853
103125
  const operands = this.flattenChain(source);
102854
- if (!operands.some(op => { const c = op.getFirstToken().getStr()[0]; return c === "'" || c === "`"; })) {
103126
+ if (!operands.some(op => this.isLiteral(op))) {
102855
103127
  continue;
102856
103128
  }
102857
103129
  const template = this.buildTemplate(operands);
@@ -102876,10 +103148,11 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102876
103148
  buildTemplate(operands) {
102877
103149
  let inner = "";
102878
103150
  for (const op of operands) {
102879
- const raw = op.getFirstToken().getStr();
102880
- const firstChar = raw[0];
102881
- if (firstChar === "'" || firstChar === "`") {
102882
- inner += raw.slice(1, -1);
103151
+ if (this.isLiteral(op)) {
103152
+ const raw = op.getFirstToken().getStr();
103153
+ const delimiter = raw[0];
103154
+ const value = raw.slice(1, -1).split(delimiter + delimiter).join(delimiter);
103155
+ inner += value.replace(/[\\|{}]/g, "\\$&");
102883
103156
  }
102884
103157
  else {
102885
103158
  inner += `{ ${op.concatTokens()} }`;
@@ -102887,6 +103160,14 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102887
103160
  }
102888
103161
  return `|${inner}|`;
102889
103162
  }
103163
+ // plain character or string literal, ie. not text symbols or concatenated constants
103164
+ isLiteral(op) {
103165
+ if (op.getFirstToken() !== op.getLastToken()) {
103166
+ return false;
103167
+ }
103168
+ const firstChar = op.getFirstToken().getStr()[0];
103169
+ return firstChar === "'" || firstChar === "`";
103170
+ }
102890
103171
  }
102891
103172
  exports.PreferStringTemplate = PreferStringTemplate;
102892
103173
  //# sourceMappingURL=prefer_string_template.js.map
@@ -115623,6 +115904,7 @@ exports.PopulateTables = PopulateTables;
115623
115904
 
115624
115905
  Object.defineProperty(exports, "__esModule", ({ value: true }));
115625
115906
  exports.DatabaseSchemaReuse = void 0;
115907
+ const OPERATORS = { EQ: "=", NE: "<>", LT: "<", LE: "<=", GT: ">", GE: ">=" };
115626
115908
  class DatabaseSchemaReuse {
115627
115909
  myQuote;
115628
115910
  constructor(quote) {
@@ -115632,31 +115914,68 @@ class DatabaseSchemaReuse {
115632
115914
  return this.myQuote + name.toLowerCase() + this.myQuote;
115633
115915
  }
115634
115916
  buildVIEW(view) {
115635
- const fields = view.getFields();
115636
- let firstTabname = "";
115637
- const columns = fields?.map((f) => {
115638
- firstTabname = this.quote(f.TABNAME.toLowerCase());
115639
- return firstTabname + "." + f.FIELDNAME.toLowerCase() + " AS " + f.VIEWFIELD.toLowerCase();
115917
+ const fields = view.getFields() || [];
115918
+ const columns = fields.map((f) => {
115919
+ return this.quote(f.TABNAME) + "." + f.FIELDNAME.toLowerCase() + " AS " + f.VIEWFIELD.toLowerCase();
115640
115920
  }).join(", ");
115641
- let from = "";
115642
- let previous = "";
115643
- for (const j of view.getJoin() || []) {
115644
- if (previous === "") {
115645
- from += this.quote(j.LTAB.toLowerCase()) + " INNER JOIN " + this.quote(j.RTAB.toLowerCase()) + " ON " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115646
- }
115647
- else if (previous === j.LTAB + "," + j.RTAB) {
115648
- from += " AND " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115649
- }
115650
- else {
115651
- from += " INNER JOIN " + this.quote(j.RTAB.toLowerCase()) + " ON " + this.quote(j.LTAB.toLowerCase()) + "." + j.LFIELD.toLowerCase() + " = " + this.quote(j.RTAB.toLowerCase()) + "." + j.RFIELD.toLowerCase();
115652
- }
115653
- previous = j.LTAB + "," + j.RTAB;
115654
- }
115655
- from = from.trim();
115656
- if (from === "") {
115657
- from = firstTabname;
115658
- }
115659
- return `CREATE VIEW ${this.quote(view.getName().toLowerCase())} AS SELECT ${columns} FROM ${from};\n`;
115921
+ // a DD28J row compares two fields, or without RFIELD a field to the constant in RTAB, eg. 'EN'
115922
+ const joins = view.getJoin() || [];
115923
+ let pending = joins.filter(j => j.RFIELD !== undefined && j.RFIELD !== "");
115924
+ const constants = joins.filter(j => j.RFIELD === undefined || j.RFIELD === "");
115925
+ // start from the table of the first view field, typically the root table, then repeatedly
115926
+ // join a table that has conditions on the tables already joined, LTAB is not always the new one
115927
+ const tables = [];
115928
+ const conditions = {};
115929
+ const add = (table) => {
115930
+ tables.push(table);
115931
+ const on = pending.filter(j => tables.includes(j.LTAB) && tables.includes(j.RTAB));
115932
+ pending = pending.filter(j => on.includes(j) === false);
115933
+ conditions[table] = [...on, ...constants.filter(j => j.LTAB === table)].map(j => this.condition(view, j));
115934
+ };
115935
+ const first = fields[0]?.TABNAME;
115936
+ const root = first !== undefined && (pending.length === 0 || pending.some(j => j.LTAB === first || j.RTAB === first))
115937
+ ? first : (pending[0] || constants[0])?.LTAB;
115938
+ if (root === undefined) {
115939
+ throw new Error(`View ${view.getName()}: no tables`);
115940
+ }
115941
+ add(root);
115942
+ while (pending.length > 0) {
115943
+ // tables without conditions on the joined ones are cross joined
115944
+ const next = pending.find(j => tables.includes(j.LTAB) || tables.includes(j.RTAB)) || pending[0];
115945
+ add(tables.includes(next.LTAB) ? next.RTAB : next.LTAB);
115946
+ }
115947
+ for (const c of constants) {
115948
+ if (tables.includes(c.LTAB) === false) {
115949
+ add(c.LTAB);
115950
+ }
115951
+ }
115952
+ let from = this.quote(tables[0]);
115953
+ for (const table of tables.slice(1)) {
115954
+ from += conditions[table].length === 0
115955
+ ? " CROSS JOIN " + this.quote(table)
115956
+ : " INNER JOIN " + this.quote(table) + " ON " + conditions[table].join(" AND ");
115957
+ }
115958
+ if (conditions[tables[0]].length > 0) {
115959
+ from += " WHERE " + conditions[tables[0]].join(" AND ");
115960
+ }
115961
+ return `CREATE VIEW ${this.quote(view.getName())} AS SELECT ${columns} FROM ${from};\n`;
115962
+ }
115963
+ condition(view, j) {
115964
+ const operator = OPERATORS[j.OPERATOR];
115965
+ if (operator === undefined) {
115966
+ throw new Error(`View ${view.getName()}: join operator ${j.OPERATOR} not supported`);
115967
+ }
115968
+ const right = j.RFIELD === undefined || j.RFIELD === ""
115969
+ ? this.escape(j.RTAB)
115970
+ : this.quote(j.RTAB) + "." + j.RFIELD.toLowerCase();
115971
+ return this.quote(j.LTAB) + "." + j.LFIELD.toLowerCase() + " " + operator + " " + right;
115972
+ }
115973
+ // the constant keeps its single quotes, statements are inside a javascript string template
115974
+ escape(value) {
115975
+ let ret = value.replace(/\\/g, "\\\\");
115976
+ ret = ret.replace(/`/g, "\\`");
115977
+ ret = ret.replace(/\${/g, "\\${");
115978
+ return ret;
115660
115979
  }
115661
115980
  }
115662
115981
  exports.DatabaseSchemaReuse = DatabaseSchemaReuse;
@@ -118863,6 +119182,7 @@ const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_
118863
119182
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
118864
119183
  const type_name_or_infer_1 = __webpack_require__(/*! ./type_name_or_infer */ "./node_modules/@abaplint/transpiler/build/src/expressions/type_name_or_infer.js");
118865
119184
  const types_1 = __webpack_require__(/*! ../types */ "./node_modules/@abaplint/transpiler/build/src/types.js");
119185
+ const field_assignment_1 = __webpack_require__(/*! ./field_assignment */ "./node_modules/@abaplint/transpiler/build/src/expressions/field_assignment.js");
118866
119186
  class NewObjectTranspiler {
118867
119187
  transpile(node, traversal) {
118868
119188
  const ret = new chunk_1.Chunk();
@@ -118907,10 +119227,48 @@ class NewObjectTranspiler {
118907
119227
  ret.appendString(traversal.toType(type) + ".set(await (new " + clas + "()).constructor_(" + para + "))");
118908
119228
  }
118909
119229
  else {
118910
- const source = node.findFirstExpression(abaplint.Expressions.Source);
119230
+ // a new data object with a copy of the value, never the source itself; async, as sources can await
118911
119231
  const typeCode = traversal.toType(type);
118912
- const sourceCode = source === undefined ? typeCode : traversal.traverse(source).getCode();
118913
- ret.appendString("((() => {const r = new abap.types.DataReference(" + typeCode + "); r.assign(" + sourceCode + "); return r; })())");
119232
+ const setCode = this.buildValue(node, type, traversal);
119233
+ ret.appendString("(await (async () => {const r = new abap.types.DataReference(" + typeCode + "); r.assign(" + typeCode + ");" + setCode + " return r; })())");
119234
+ }
119235
+ return ret;
119236
+ }
119237
+ buildValue(node, type, traversal) {
119238
+ let ret = "";
119239
+ const parameters = node.findDirectExpression(core_1.Expressions.ParameterListS);
119240
+ if (parameters) {
119241
+ // NEW ty( a = 1 b = 2 ), components of a structure
119242
+ for (const p of parameters.findDirectExpressions(core_1.Expressions.ParameterS)) {
119243
+ const name = p.findDirectExpression(core_1.Expressions.ParameterName).concatTokens().toLowerCase();
119244
+ const source = traversal.traverse(p.findDirectExpression(core_1.Expressions.Source)).getCode();
119245
+ ret += ` r.getPointer().setField("${name}", ${source});`;
119246
+ }
119247
+ return ret;
119248
+ }
119249
+ const single = node.getChildren()[3];
119250
+ if (single instanceof core_1.Nodes.ExpressionNode && single.get() instanceof core_1.Expressions.Source) {
119251
+ return ` r.getPointer().set(${traversal.traverse(single).getCode()});`;
119252
+ }
119253
+ else if (!(type instanceof abaplint.BasicTypes.TableType)) {
119254
+ return ret;
119255
+ }
119256
+ // NEW tab( ( 1 ) ( a = 2 ) ), table lines
119257
+ const rowType = traversal.toType(type.getRowType());
119258
+ let row = "";
119259
+ for (const c of node.getChildren().slice(3)) {
119260
+ if (c instanceof core_1.Nodes.TokenNode) {
119261
+ if (c.getFirstToken().getStr() === ")" && row !== "") {
119262
+ ret += ` r.getPointer().appendThis(${rowType}${row});`;
119263
+ row = "";
119264
+ }
119265
+ }
119266
+ else if (c.get() instanceof core_1.Expressions.Source) {
119267
+ row += `.set(${traversal.traverse(c).getCode()})`;
119268
+ }
119269
+ else if (c.get() instanceof core_1.Expressions.FieldAssignment && c instanceof core_1.Nodes.ExpressionNode) {
119270
+ row += new field_assignment_1.FieldAssignmentTranspiler().transpile(c, traversal).getCode();
119271
+ }
118914
119272
  }
118915
119273
  return ret;
118916
119274
  }
@@ -135039,6 +135397,9 @@ class ClassImplementationTranspiler {
135039
135397
  transpile(node, traversal) {
135040
135398
  const ret = new chunk_1.Chunk();
135041
135399
  for (const c of node.getChildren()) {
135400
+ if (c.get() instanceof abaplint.Statements.EndClass) {
135401
+ ret.appendString(this.buildPartiallyImplementedStubs(node, traversal));
135402
+ }
135042
135403
  ret.appendChunk(traversal.traverse(c));
135043
135404
  if (c.get() instanceof abaplint.Statements.ClassImplementation) {
135044
135405
  ret.appendString(this.buildConstructor(node.getFirstStatement(), traversal));
@@ -135054,6 +135415,103 @@ class ClassImplementationTranspiler {
135054
135415
  return ret;
135055
135416
  }
135056
135417
  ///////////////////////////////
135418
+ /** INTERFACES ... PARTIALLY IMPLEMENTED: the interface methods the test class
135419
+ * does not implement exist on a system and raise CX_SY_DYN_CALL_ILLEGAL_METHOD
135420
+ * when called. Emits a method of that shape under the name the implementation
135421
+ * would have had, so the call ends in that exception and not in a TypeError */
135422
+ buildPartiallyImplementedStubs(node, traversal) {
135423
+ const token = node.findFirstExpression(abaplint.Expressions.ClassName)?.getFirstToken();
135424
+ if (token === undefined) {
135425
+ return "";
135426
+ }
135427
+ const cdef = traversal.getClassDefinition(token);
135428
+ const scope = traversal.findCurrentScopeByToken(token);
135429
+ if (cdef === undefined || scope === undefined) {
135430
+ return "";
135431
+ }
135432
+ const partial = cdef.getImplementing().filter(i => i.partial === true);
135433
+ if (partial.length === 0) {
135434
+ return "";
135435
+ }
135436
+ // the methods the class implements itself, by the emitted name
135437
+ const implemented = new Set();
135438
+ for (const m of node.findAllStatements(abaplint.Statements.MethodImplementation)) {
135439
+ const name = m.findFirstExpression(abaplint.Expressions.MethodName)?.getFirstToken().getStr();
135440
+ if (name !== undefined) {
135441
+ implemented.add(traversal_1.Traversal.escapeNamespace(name.replace("~", "$").toLowerCase()));
135442
+ }
135443
+ }
135444
+ // an interface a superclass implements is inherited, with the real implementations,
135445
+ // a stub for one of its methods would override those
135446
+ const inherited = new Set();
135447
+ let sup = cdef.getSuperClass();
135448
+ while (sup !== undefined) {
135449
+ const sdef = traversal.findClassDefinition(sup, scope);
135450
+ for (const name of this.findIncludedInterfaces(sdef?.getImplementing().map(i => i.name) || [], scope, traversal)) {
135451
+ inherited.add(name);
135452
+ }
135453
+ sup = sdef?.getSuperClass();
135454
+ }
135455
+ let ret = "";
135456
+ for (const name of this.findIncludedInterfaces(partial.map(i => i.name), scope, traversal)) {
135457
+ if (inherited.has(name)) {
135458
+ continue;
135459
+ }
135460
+ const intf = traversal.findInterfaceDefinition(name, scope);
135461
+ if (intf === undefined) {
135462
+ continue;
135463
+ }
135464
+ const prefix = traversal_1.Traversal.escapeNamespace(intf.getName().toLowerCase()) + "$";
135465
+ for (const m of intf.getMethodDefinitions().getAll()) {
135466
+ const emitted = prefix + traversal_1.Traversal.escapeNamespace(m.getName().toLowerCase());
135467
+ if (implemented.has(emitted)) {
135468
+ continue;
135469
+ }
135470
+ implemented.add(emitted);
135471
+ ret += this.buildNotImplementedMethod(emitted, m, token.getStr().toLowerCase(), intf.getName(), token, traversal);
135472
+ }
135473
+ }
135474
+ return ret;
135475
+ }
135476
+ /** the interfaces, upper cased, with the interfaces they include, recursively */
135477
+ findIncludedInterfaces(names, scope, traversal) {
135478
+ const ret = [];
135479
+ const queue = [...names];
135480
+ while (queue.length > 0) {
135481
+ const name = queue.shift().toUpperCase();
135482
+ if (ret.includes(name)) {
135483
+ continue;
135484
+ }
135485
+ ret.push(name);
135486
+ const intf = traversal.findInterfaceDefinition(name, scope);
135487
+ queue.push(...(intf?.getImplementing().map(i => i.name) || []));
135488
+ }
135489
+ return ret;
135490
+ }
135491
+ /** the method a system generates for an interface method the test class leaves
135492
+ * out under PARTIALLY IMPLEMENTED: calling it raises CX_SY_DYN_CALL_ILLEGAL_METHOD
135493
+ * (ABAP keyword documentation, INTERFACES - PARTIALLY IMPLEMENTED), the class and
135494
+ * method named, instead of a JavaScript TypeError no CATCH sees */
135495
+ buildNotImplementedMethod(emitted, m, className, interfaceName, token, traversal) {
135496
+ const unique = m.getParameters().getAll().length > 0 ? "INPUT" : "";
135497
+ const exception = traversal.lookupClassOrInterface("'CX_SY_DYN_CALL_ILLEGAL_METHOD'", token, true);
135498
+ const classUpper = className.toUpperCase();
135499
+ const methodUpper = interfaceName.toUpperCase() + "~" + m.getName().toUpperCase();
135500
+ const notFound = `CX_SY_DYN_CALL_ILLEGAL_METHOD not found, ${classUpper}->${methodUpper} is not implemented`;
135501
+ const body = `if (${exception} === undefined) { throw new Error("${notFound}"); }\n` +
135502
+ `throw await new ${exception}().constructor_({` +
135503
+ `classname: new abap.types.String().set("${classUpper}"), ` +
135504
+ `methodname: new abap.types.String().set("${methodUpper}")});\n`;
135505
+ let ret = "";
135506
+ if (m.isStatic() === true) {
135507
+ // in ABAP static methods can be called with instance arrows, "->"
135508
+ ret += "async " + emitted + "(" + unique + ") {\n" +
135509
+ "return " + traversal_1.Traversal.escapeNamespace(className) + "." + emitted + "(" + unique + ");\n" +
135510
+ "}\n" + "static ";
135511
+ }
135512
+ ret += "async " + emitted + "(" + unique + ") {\n" + body + "}\n";
135513
+ return ret;
135514
+ }
135057
135515
  hasConstructor(node) {
135058
135516
  for (const m of node.findAllStatements(abaplint.Statements.MethodImplementation)) {
135059
135517
  const name = m.findFirstExpression(abaplint.Expressions.MethodName)?.getFirstToken().getStr();
package/build/types.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface ITranspilerConfig {
35
35
  */
36
36
  name?: string;
37
37
  url?: string;
38
+ /** commit SHA, tag or branch to check out from url; defaults to the default branch */
39
+ ref?: string;
38
40
  /** relative to the current working directory */
39
41
  folder?: string;
40
42
  files?: string | string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler-cli",
3
- "version": "2.14.11",
3
+ "version": "2.14.13",
4
4
  "description": "Transpiler - Command Line Interface",
5
5
  "funding": "https://github.com/sponsors/larshp",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "author": "abaplint",
28
28
  "license": "MIT",
29
29
  "devDependencies": {
30
- "@abaplint/core": "^2.120.86",
31
- "@abaplint/transpiler": "^2.14.11",
30
+ "@abaplint/core": "^2.120.89",
31
+ "@abaplint/transpiler": "^2.14.13",
32
32
  "@types/glob": "^9.0.0",
33
33
  "@types/node": "^26.6.5",
34
34
  "@types/progress": "^2.0.7",