Skip to content

Commit

Permalink
Remove semantic analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladiwostok committed Aug 17, 2024
1 parent baaf21f commit f95254e
Showing 1 changed file with 122 additions and 28 deletions.
150 changes: 122 additions & 28 deletions src/dscanner/analysis/unused_result.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,84 @@ extern (C++) class UnusedResultChecker(AST) : BaseAnalyzerDmd
{
alias visit = BaseAnalyzerDmd.visit;
mixin AnalyzerInfo!"unused_result";

private enum KEY = "dscanner.performance.enum_array_literal";
private enum string MSG = "Function return value is discarded";

private bool[string] nonVoidFuncs;
private string[] aggregateStack;

extern (D) this(string fileName, bool skipTests = false)
{
super(fileName, skipTests);
}

private template VisitAggregate(NodeType)
{
override void visit(NodeType aggregate)
{
string name = cast(string) aggregate.ident.toString();
aggregateStack ~= name;
super.visit(aggregate);
aggregateStack.length -= 1;
}
}

mixin VisitAggregate!(AST.StructDeclaration);
mixin VisitAggregate!(AST.ClassDeclaration);

override void visit(AST.FuncDeclaration funcDeclaration)
{
import dmd.astenums : TY;

auto typeFunc = funcDeclaration.type.isTypeFunction();
if (typeFunc !is null && typeFunc.next.ty != TY.Tvoid && typeFunc.next.ty != TY.Tnoreturn)
{
auto typeIdent = typeFunc.next.isTypeIdentifier();
bool isNoReturn = typeIdent is null ? false : typeIdent.ident.toString() == "noreturn";

if (!isNoReturn)
{
string funcName = buildFullyQualifiedName(cast(string) funcDeclaration.ident.toString());
nonVoidFuncs[funcName] = true;
}
}

super.visit(funcDeclaration);
}

override void visit(AST.AliasDeclaration aliasDecl)
{
import std.algorithm : canFind, endsWith;
import std.array : replace;

auto typeIdent = aliasDecl.type.isTypeIdentifier();
if (typeIdent is null)
return;

string aliasName = cast(string) aliasDecl.ident.toString();
string targetName = cast(string) typeIdent.ident.toString();

foreach(func; nonVoidFuncs.byKey())
{
if (func.endsWith(targetName) || func.canFind(targetName ~ "."))
{
string newAliasName = func.replace(targetName, aliasName);
nonVoidFuncs[newAliasName] = true;
}
}
}

private extern (D) string buildFullyQualifiedName(string funcName)
{
import std.algorithm : fold;

if (aggregateStack.length == 0)
return funcName;

return aggregateStack.fold!((a, b) => a ~ "." ~ b) ~ "." ~ funcName;
}

mixin VisitInstructionBlock!(AST.WhileStatement);
mixin VisitInstructionBlock!(AST.ForStatement);
mixin VisitInstructionBlock!(AST.DoStatement);
Expand Down Expand Up @@ -73,6 +143,21 @@ extern (C++) class UnusedResultChecker(AST) : BaseAnalyzerDmd
super.visit(ifStatement);
}

private template VisitInstructionBlock(NodeType)
{
override void visit(NodeType statement)
{
if (hasUnusedResult(statement._body))
{
auto lineNum = cast(ulong) statement._body.loc.linnum;
auto charNum = cast(ulong) statement._body.loc.charnum;
addErrorMessage(lineNum, charNum, KEY, MSG);
}

super.visit(statement);
}
}

private bool hasUnusedResult(AST.Statement statement)
{
import dmd.astenums : TY;
Expand All @@ -82,30 +167,38 @@ extern (C++) class UnusedResultChecker(AST) : BaseAnalyzerDmd
return false;

auto callExpr = exprStatement.exp.isCallExp();
if (callExpr is null || callExpr.f is null)
if (callExpr is null)
return false;

auto typeFunction = callExpr.f.type.isTypeFunction();
if (typeFunction is null)
return false;
string funcName = "";

TY type = typeFunction.next.ty;
return type != TY.Tvoid && type != TY.Tnoreturn;
if (auto identExpr = callExpr.e1.isIdentifierExp())
funcName = cast(string) identExpr.ident.toString();
else if (auto dotIdExpr = callExpr.e1.isDotIdExp())
funcName = buildFullyQualifiedCallName(dotIdExpr);

return (funcName in nonVoidFuncs) !is null;
}

private template VisitInstructionBlock(T)
private extern (D) string buildFullyQualifiedCallName(AST.DotIdExp dotIdExpr)
{
override void visit(T statement)
{
if (hasUnusedResult(statement._body))
{
auto lineNum = cast(ulong) statement._body.loc.linnum;
auto charNum = cast(ulong) statement._body.loc.charnum;
addErrorMessage(lineNum, charNum, KEY, MSG);
}
import std.algorithm : fold, reverse;

super.visit(statement);
string[] nameStack;
nameStack ~= cast(string) dotIdExpr.ident.toString();

auto lastExpr = dotIdExpr.e1;
while (lastExpr.isDotIdExp())
{
auto current = lastExpr.isDotIdExp();
nameStack ~= cast(string) current.ident.toString();
lastExpr = current.e1;
}

if (auto identExpr = lastExpr.isIdentifierExp())
nameStack ~= cast(string) identExpr.ident.toString();

return nameStack.reverse.fold!((a, b) => a ~ "." ~ b);
}
}

Expand All @@ -126,7 +219,7 @@ unittest
{
fun();
}
}c, sac, true);
}c, sac);

assertAnalyzerWarningsDMD(q{
alias noreturn = typeof(*null);
Expand All @@ -135,15 +228,15 @@ unittest
{
fun();
}
}c, sac, true);
}c, sac);

assertAnalyzerWarningsDMD(q{
int fun() { return 1; }
void main()
{
fun(); // [warn]: %s
}
}c.format(MSG), sac, true);
}c.format(MSG), sac);

assertAnalyzerWarningsDMD(q{
struct Foo
Expand All @@ -157,32 +250,33 @@ unittest
void main()
{
Bar.get(); // [warn]: %s
Foo.bar.get();
}
}c.format(MSG), sac, true);
}c.format(MSG), sac);

assertAnalyzerWarningsDMD(q{
void main()
{
void fun() {}
fun();
}
}c, sac, true);
}c, sac);

assertAnalyzerWarningsDMD(q{
void main()
{
int fun() { return 1; }
fun(); // [warn]: %s
}
}c.format(MSG), sac, true);
}c.format(MSG), sac);

assertAnalyzerWarningsDMD(q{
int fun() { return 1; }
void main()
{
cast(void) fun();
}
}c, sac, true);
}c, sac);

assertAnalyzerWarningsDMD(q{
void fun() { }
Expand All @@ -191,7 +285,7 @@ unittest
{
gun();
}
}c, sac, true);
}c, sac);

assertAnalyzerWarningsDMD(q{
int fun() { return 1; }
Expand All @@ -202,7 +296,7 @@ unittest
else
fun(); // [warn]: %s
}
}c.format(MSG, MSG), sac, true);
}c.format(MSG, MSG), sac);

assertAnalyzerWarningsDMD(q{
int fun() { return 1; }
Expand All @@ -211,7 +305,7 @@ unittest
while (true)
fun(); // [warn]: %s
}
}c.format(MSG), sac, true);
}c.format(MSG), sac);

assertAnalyzerWarningsDMD(q{
int fun() { return 1; }
Expand All @@ -220,15 +314,15 @@ unittest
{
gun(); // [warn]: %s
}
}c.format(MSG), sac, true);
}c.format(MSG), sac);

assertAnalyzerWarningsDMD(q{
void main()
{
void fun() {}
fun();
}
}c, sac, true);
}c, sac);

stderr.writeln("Unittest for UnusedResultChecker passed");
}

0 comments on commit f95254e

Please sign in to comment.