// TODO: sometimes equals/hashCode is not generated. See an old version of #1026301 sbool tok_recordDecls_autoWithToList = true; static void tok_recordDecls(L tok) { int i; bool re = false; jreplace(tok, "record > {", "record $2 extends $4 {"); jreplace(tok, "record > <> {", "record $2 extends $4 $5 $6 $7 {"); jreplace(tok, "record {", "record $2() {"); jreplace(tok, "record implements", "record $2() implements"); jreplace(tok, "record extends", "record $2() extends"); while ((i = jfind_any(tok, "record (", "record <")) >= 0) { int argsFrom = smartIndexOf(tok, i, "(")+2; int argsTo = findCodeTokens(tok, argsFrom, false, ")"); int idx = findCodeTokens(tok, argsTo, false, "{"); ifdef tok_recordDecls_debug assertEqualsVerbose("{", get(tok, idx)); endifdef int j = findEndOfBracketPart(tok, idx); S name = tok.get(i+2); int iMod = tok_leftScanCustomModifiers(tok, i, addAllAndReturnCollection(litset("noeq", "flexeq", "noToString", "withToList", "transformable"), getJavaModifiers()); Set mods = codeTokensAsSet(subList(tok, iMod-1, i)); clearTokensExceptFromSet(tok, iMod, i-1, getJavaModifiers()); bool withToList = mods.contains("withToList"); withToList = withToList || tok_recordDecls_autoWithToList; new StringBuilder buf; L tArgs = subList(tok, argsFrom-1, argsTo); tok_typesAndNamesOfParams_keepModifiers.set(true); LPairS argsWithModifiers = tok_typesAndNamesOfParams(tArgs, typelessMeansObject := true); LPairS args = tok_typesAndNamesOfParams(tArgs, typelessMeansObject := true); L contents = subList(tok, idx+1, j); bool hasDefaultConstructor = tok_hasDefaultConstructor(contents, name); // Make fields for (Pair p : argsWithModifiers) buf.append("\n " + jreplace(p.a, "...", "[]") + " " + p.b + ";"); if (nempty(args) && !hasDefaultConstructor) buf.append("\n *() {}"); // add full constructor if not there S ctorHead = "\n *(" + joinWithComma(map(args, p -> dropPrefix("new ", p.a) + " *" + p.b)) + ")"; var comments = trimAll(getJavaLineCommentsWithoutNestedBlocks(contents)); printVars(+ctorHead, +comments); if (!contains(comments, "!customConstructor") && jfind(contents, tok_escapeAsterisk(ctorHead)) < 0) buf.append(ctorHead + " {}"); // Make toString if (!mods.contains("noToString") && !(jfind(contents, "toString {") >= 0 || jfind(contents, "toString()") >= 0)) buf.append("\n toString { ret " + "shortClassName_dropNumberPrefix(this)" + (empty(args) ? "" : [[ + "(" + ]] + join([[ + ", " + ]], secondOfPairs(args)) + [[ + ")"]]) + "; }"); // Make equals and hashCode if (!mods.contains("noeq")) { //buf.append("\n [stdEq]"); bool flexEq = mods.contains("flexeq"); // fix for records inside of parameterized classes buf.append(tok_genEqualsAndHashCode(name, args, +flexEq)); } if (withToList) buf.append(tok_genRecordFieldsToList(args)); bool transformable = mods.contains("transformable"); if (transformable) buf.append(tok_genRecordTransformable(name, args)); S interfaces = joinNemptiesWithComma( withToList ? "IFieldsToList" : "", transformable ? "Transformable, Visitable" : ""); tok.set(idx, (empty(interfaces) ? "" : (contains(subList(tok, argsTo, idx), "implements") ? "," : "implements") + " " + interfaces) + "{" + buf); ifdef tok_recordDecls_debug print("tok_recordDecls replaced " + tok.get(idx)); print("tok_recordDecls whole: " + join(tok)); endifdef tok.set(i, "class"); clearTokens(tok, argsFrom-2, argsTo+1); reTok(tok, i, idx+1); // no static fields allowed in non-static classes (for whatever reason) if (contains(tok_modifiersLeftOf(tok, i), "static")) tok_addFieldOrder(tok, i); ifdef tok_recordDecls_debug print("tok_recordDecls whole 2: " + join(tok)); endifdef set re; } if (re) reTok(tok); }