Merge branch 'vardec_varass_dependency' into feature/multi_module

This commit is contained in:
Tristan B. Velloza Kildaire 2024-04-07 12:11:03 +02:00
commit 0a3a543f98
9 changed files with 189 additions and 2 deletions

View File

@ -382,6 +382,11 @@ jobs:
else
exit 1
fi
- name: Unused variables check (positive case)
run: ./tlang typecheck source/tlang/testing/unused_vars.t --unusedVars true
- name: Unused variables check (negative case)
run: ./tlang typecheck source/tlang/testing/unused_vars_none.t --unusedVars true
emit:
needs: [build, unittests]

View File

@ -129,7 +129,15 @@ mixin template EmitBase()
*/
mixin template TypeCheckerBase()
{
@ArgNamed("unusedVars|uvars", "Warn about any unused variables")
@(ArgConfig.optional)
bool warnUnusedVariables = true;
void TypeCheckerInit(Compiler compiler)
{
// Set whether to warn about unused variables
compiler.getConfig().addConfig(ConfigEntry("typecheck:warnUnusedVars", warnUnusedVariables));
}
}
/**
@ -165,6 +173,9 @@ struct compileCommand
/* Setup general configuration parameters */
BaseCommandInit(compiler);
/* Setup all type checker related parameters */
TypeCheckerInit(compiler);
/* Perform tokenization */
compiler.doLex();
writeln("=== Tokens ===\n");
@ -331,6 +342,9 @@ struct typecheckCommand
/* Setup general configuration parameters */
BaseCommandInit(compiler);
/* Setup all type checker related parameters */
TypeCheckerInit(compiler);
compiler.doLex();
writeln("=== Tokens ===\n");
writeln(compiler.getTokens());

View File

@ -246,6 +246,9 @@ public final class CompilerConfiguration
// FIXME: Make true by default - this WILL break many unittests and semantic ones
// so be very sure before you enable this
config.addConfig(ConfigEntry("modman:strict_headers", false));
/* Always warn about unused variables */
config.addConfig(ConfigEntry("typecheck:warnUnusedVars", true));
return config;
}

View File

@ -246,9 +246,22 @@ public class Compiler
this.typeChecker.beginCheck();
}
/**
* Returns the type checker instance of
* this compiler
*
* Returns: the type checker
* Throws:
* CompilerException if you have not
* called `doTypeCheck()` yet
*/
public TypeChecker getTypeChecker()
{
// TODO: Should we do a check for if `doTypeCheck()` was called or not?
if(typeChecker is null)
{
throw new CompilerException(CompilerError.TYPECHECK_NOT_YET_PERFORMED);
}
return this.typeChecker;
}

View File

@ -242,6 +242,34 @@ public final class TypeChecker
*/
initsScratchToModQueue(mod);
}
/* Collect statistics */
doPostChecks();
}
/**
* These are just checks we run for the convenience
* of the user. They do not manipulate anything but
* rather provide statistics
*/
private void doPostChecks()
{
/**
* Find the variables which were declared but never used
*/
if(this.config.hasConfig("typecheck:warnUnusedVars") & this.config.getConfig("typecheck:warnUnusedVars").getBoolean())
{
Variable[] unusedVariables = getUnusedVariables();
gprintln("There are "~to!(string)(unusedVariables.length)~" unused variables");
if(unusedVariables.length)
{
foreach(Variable unusedVariable; unusedVariables)
{
// TODO: Get a nicer name, full path-based
gprintln("Variable '"~to!(string)(unusedVariable.getName())~"' is declared but never used");
}
}
}
}
/**
@ -3458,6 +3486,51 @@ public final class TypeChecker
//assert()
}
/**
* Maps a given `Variable` to its reference
* count. This includes the declaration
* thereof.
*/
private uint[Variable] varRefCounts;
/**
* Increments the given variable's reference
* count
*
* Params:
* variable = the variable
*/
void touch(Variable variable)
{
// Create entry if not existing yet
if(variable !in this.varRefCounts)
{
this.varRefCounts[variable] = 0;
}
// Increment count
this.varRefCounts[variable]++;
}
/**
* Returns all variables which were declared
* but not used
*
* Returns: the array of variables
*/
public Variable[] getUnusedVariables()
{
Variable[] unused;
foreach(Variable variable; this.varRefCounts.keys())
{
if(!(this.varRefCounts[variable] > 1))
{
unused ~= variable;
}
}
return unused;
}
}
@ -3796,4 +3869,64 @@ unittest
compiler.doTypeCheck();
/* TODO: Actually test generated code queue */
}
/**
* Tests the unused variable detection mechanism
*
* Case: Positive (unused variables exist)
* Source file: source/tlang/testing/unused_vars.t
*/
unittest
{
// Dummy field out
File fileOutDummy;
import tlang.compiler.core;
string sourceFile = "source/tlang/testing/unused_vars.t";
Compiler compiler = new Compiler(gibFileData(sourceFile), fileOutDummy);
compiler.doLex();
compiler.doParse();
compiler.doTypeCheck();
TypeChecker tc = compiler.getTypeChecker();
/**
* There should be 1 unused variable and then
* it should be named `j`
*/
Variable[] unusedVars = tc.getUnusedVariables();
assert(unusedVars.length == 1);
Variable unusedVarActual = unusedVars[0];
Variable unusedVarExpected = cast(Variable)tc.getResolver().resolveBest(tc.getModule(), "j");
assert(unusedVarActual is unusedVarExpected);
}
/**
* Tests the unused variable detection mechanism
*
* Case: Negative (unused variables do NOT exist)
* Source file: source/tlang/testing/unused_vars_none.t
*/
unittest
{
// Dummy field out
File fileOutDummy;
import tlang.compiler.core;
string sourceFile = "source/tlang/testing/unused_vars_none.t";
Compiler compiler = new Compiler(gibFileData(sourceFile), fileOutDummy);
compiler.doLex();
compiler.doParse();
compiler.doTypeCheck();
TypeChecker tc = compiler.getTypeChecker();
/**
* There should be 0 unused variables
*/
Variable[] unusedVars = tc.getUnusedVariables();
assert(unusedVars.length == 0);
}

View File

@ -734,6 +734,9 @@ public class DNodeGenerator
/* Get the entity as a Variable */
Variable variable = cast(Variable)namedEntity;
/* Variable reference count must increase */
tc.touch(variable);
/* Pool the node */
VariableNode varDecNode = poolT!(VariableNode, Variable)(variable);
@ -1041,6 +1044,9 @@ public class DNodeGenerator
writeln("Hello");
writeln("VarType: "~to!(string)(variableType));
/* Add an entry to the reference counting map */
tc.touch(variable);
/* Basic type */
if(cast(Primitive)variableType)
{
@ -1143,6 +1149,9 @@ public class DNodeGenerator
Variable variable = cast(Variable)tc.getResolver().resolveBest(c, vAsStdAl.getVariableName());
assert(variable);
/* Assinging to a variable is usage, therefore increment the reference count */
tc.touch(variable);
/* Pool the variable */
DNode varDecDNode = pool(variable);
@ -1620,5 +1629,4 @@ public class DNodeGenerator
return classDNode;
}
}

View File

@ -0,0 +1,3 @@
module unused_vars;
int j;

View File

@ -0,0 +1,8 @@
module unused_vars;
int j;
void thing()
{
j = 1;
}

BIN
tlang

Binary file not shown.