267 lines
6.2 KiB
D
267 lines
6.2 KiB
D
// This generates functions with all specified test types as first argument +
|
|
// variables for each specified test type.
|
|
// Then it calls all functions with every type to see which ones are accepted by
|
|
// the compiler, to automatically stay up-to-date.
|
|
|
|
import std;
|
|
import fs = std.file;
|
|
|
|
string[] testTypes = [
|
|
"bool",
|
|
"byte",
|
|
"ubyte",
|
|
"short",
|
|
"ushort",
|
|
"int",
|
|
"uint",
|
|
"long",
|
|
"ulong",
|
|
"char",
|
|
"wchar",
|
|
"dchar",
|
|
"float",
|
|
"double",
|
|
"real",
|
|
"BasicStruct",
|
|
"AliasThisInt",
|
|
];
|
|
// index here must map onto varTypePermutations index
|
|
string[][] funcTypePermutations = [
|
|
// TODO: check for const/inout/immutable/shared in UFCS checks
|
|
[
|
|
"%s",
|
|
// "const(%s)",
|
|
"ref %s",
|
|
// "ref const(%s)"
|
|
],
|
|
[
|
|
"%s*",
|
|
// "const(%s)*",
|
|
// "const(%s*)",
|
|
"ref %s*",
|
|
// "ref const(%s)*",
|
|
// "ref const(%s*)"
|
|
],
|
|
[
|
|
"%s[]",
|
|
// "const(%s)[]",
|
|
// "const(%s[])",
|
|
"ref %s[]",
|
|
// "ref const(%s)[]",
|
|
// "ref const(%s[])"
|
|
]
|
|
];
|
|
string[][] varTypePermutations = [
|
|
[
|
|
"%s",
|
|
// "const(%s)"
|
|
],
|
|
[
|
|
"%s*",
|
|
// "const(%s)*",
|
|
// "const(%s*)"
|
|
],
|
|
[
|
|
"%s[]",
|
|
// "const(%s)[]",
|
|
// "const(%s[])"
|
|
]
|
|
];
|
|
|
|
string preamble = `
|
|
struct BasicStruct { int member1; string member2; }
|
|
struct AliasThisInt { int member1; string member2; alias member1 this; }
|
|
|
|
`;
|
|
|
|
int main(string[] args)
|
|
{
|
|
string functionsCode;
|
|
string varsCode;
|
|
string callCode;
|
|
|
|
string[] allFunctions;
|
|
string[string] funcLookup;
|
|
string[string] varLookup;
|
|
|
|
foreach (ti, type; testTypes)
|
|
{
|
|
foreach (pi, perms; funcTypePermutations)
|
|
{
|
|
foreach (i, perm; perms)
|
|
{
|
|
string resolved = format(perm, type);
|
|
string id = getID(ti, pi, i);
|
|
allFunctions ~= ("func_" ~ id);
|
|
functionsCode ~= "void func_" ~ id ~ "(" ~ resolved ~ " arg) {}\n";
|
|
functionsCode ~= resolved ~ " make_" ~ id ~ "() { static " ~ resolved
|
|
.chompPrefix("ref ") ~ " x; return x; }\n";
|
|
funcLookup["func_" ~ id] = resolved;
|
|
}
|
|
}
|
|
foreach (pi, perms; varTypePermutations)
|
|
{
|
|
foreach (i, perm; perms)
|
|
{
|
|
string resolved = format(perm, type);
|
|
string id = getID(ti, pi, i);
|
|
varsCode ~= resolved ~ " var_" ~ id ~ " = make_" ~ id ~ "();\n";
|
|
varLookup["var_" ~ id] = resolved;
|
|
foreach (cti, subType; testTypes)
|
|
foreach (ci, subPerms; funcTypePermutations)
|
|
foreach (fi, subPerm; subPerms)
|
|
{
|
|
callCode ~= "var_" ~ id ~ ".func_" ~ getID(cti, ci, fi) ~ "();\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
allFunctions.sort!"a<b";
|
|
|
|
string code = preamble
|
|
~ functionsCode
|
|
~ "\nvoid main() {\n"
|
|
~ varsCode
|
|
~ callCode
|
|
~ "}\n";
|
|
string[] lines = code.splitLines;
|
|
|
|
fs.write("proc_test.d", code);
|
|
|
|
auto output = executeShell("$DC -verrors=0 -c proc_test.d").output;
|
|
|
|
size_t numErrors = 0;
|
|
|
|
string[][string] variableIncompatibilities;
|
|
|
|
foreach (err; output.lineSplitter)
|
|
{
|
|
if (!err.startsWith("proc_test.d("))
|
|
continue;
|
|
err = err["proc_test.d(".length .. $];
|
|
auto lineNo = err.parse!int;
|
|
if (!err.startsWith("): Error: "))
|
|
continue;
|
|
err = err["): Error: ".length .. $];
|
|
string line = lines[lineNo - 1];
|
|
enforce(line.endsWith("();"), "Unexpected error in line " ~ lineNo.to!string);
|
|
line = line[0 .. $ - 3];
|
|
string varName = line.findSplit(".")[0];
|
|
string funcName = line.findSplit(".")[2];
|
|
// writeln("variable type ", varLookup[varName], " can't call ", funcLookup[funcName]);
|
|
variableIncompatibilities[varName] ~= funcName;
|
|
numErrors++;
|
|
}
|
|
|
|
enforce(numErrors > 1_000, "compiler didn't error as expected, need to adjust tests!");
|
|
|
|
writeln("Total incompatible type combinations: ", numErrors);
|
|
|
|
string[][string] wrongDCDCompletions;
|
|
|
|
foreach (varName; varLookup.byKey)
|
|
{
|
|
string input = code[0 .. $ - 2]
|
|
~ "\n"
|
|
~ varName ~ ".func_";
|
|
|
|
string[] dcdClient = ["../../../bin/dcd-client"];
|
|
if (args[1].length)
|
|
dcdClient ~= args[1];
|
|
|
|
auto proc = pipeProcess(dcdClient ~ ["-c" ~ to!string(input.length)]);
|
|
proc.stdin.rawWrite(input);
|
|
proc.stdin.rawWrite("\n}\n");
|
|
proc.stdin.close();
|
|
|
|
string[] dcdResult;
|
|
|
|
size_t i = 0;
|
|
foreach (line; proc.stdout.byLineCopy)
|
|
{
|
|
if (i++ == 0)
|
|
{
|
|
enforce(line == "identifiers");
|
|
continue;
|
|
}
|
|
|
|
auto parts = line.split("\t");
|
|
if (parts[1] != "F")
|
|
continue;
|
|
dcdResult ~= parts[0];
|
|
}
|
|
|
|
enforce(i > 0, "every variable must auto-complete something! Missing completion for var " ~ varName
|
|
~ " of type " ~ varLookup[varName] ~ generateEmptyResponseReproductionCode(
|
|
varLookup[varName]));
|
|
enforce(dcdResult.length > 0, "Wrongly no UFCS completion for var " ~ varName
|
|
~ " of type " ~ varLookup[varName] ~ generateEmptyResponseReproductionCode(
|
|
varLookup[varName]));
|
|
|
|
dcdResult.sort!"a<b";
|
|
|
|
string[] minusExpect = variableIncompatibilities[varName];
|
|
minusExpect.sort!"a<b";
|
|
variableIncompatibilities[varName] = minusExpect = minusExpect.uniq.array;
|
|
|
|
auto neededFunctions = setDifference(allFunctions, minusExpect);
|
|
|
|
auto unneccessaryFunctions = setDifference(dcdResult, neededFunctions);
|
|
auto missingFunctions = setDifference(neededFunctions, setIntersection(
|
|
dcdResult, neededFunctions));
|
|
|
|
string[] diff =
|
|
unneccessaryFunctions.map!(ln => '+' ~ ln)
|
|
.chain(missingFunctions.map!(ln => '-' ~ ln))
|
|
.array;
|
|
|
|
// writeln(varLookup[varName], " -> ", dcdResult);
|
|
if (diff.length)
|
|
wrongDCDCompletions[varName] = diff;
|
|
}
|
|
|
|
foreach (varName, wrongTypes; wrongDCDCompletions)
|
|
{
|
|
writeln("Incorrect results for ", varLookup[varName], ":");
|
|
wrongTypes.sort!"a<b";
|
|
char prevChar = ' ';
|
|
foreach (wrongType; wrongTypes)
|
|
{
|
|
if (prevChar != wrongType[0])
|
|
{
|
|
prevChar = wrongType[0];
|
|
if (prevChar == '+')
|
|
writeln("\tDCD errornously matched these argument types:");
|
|
else if (prevChar == '-')
|
|
writeln("\tDCD errornously did not match these argument types:");
|
|
}
|
|
|
|
wrongType = wrongType[1 .. $];
|
|
writeln("\t\t", funcLookup[wrongType]);
|
|
}
|
|
writeln();
|
|
}
|
|
|
|
return wrongDCDCompletions.length ? 1 : 0;
|
|
}
|
|
|
|
string getID(size_t ti, size_t pi, size_t i)
|
|
{
|
|
return format!"%s_%s_%s"(ti, pi, i);
|
|
}
|
|
|
|
string generateEmptyResponseReproductionCode(string type)
|
|
{
|
|
string prefix =
|
|
"void ufcsFunc(" ~ type ~ " v) {}\n\n"
|
|
~ "void main() {\n"
|
|
~ " " ~ type ~ " myVar;\n"
|
|
~ " myVar.ufcs";
|
|
return "\n\nReproduction code:\n```d\n"
|
|
~ prefix ~ ";\n"
|
|
~ "}\n"
|
|
~ "```\n\n"
|
|
~ "call `dcd-client -c" ~ prefix.length.to!string ~ "`";
|
|
}
|