Implement #191
This commit is contained in:
parent
622c5e75cb
commit
7d5dd3bfde
|
@ -56,4 +56,7 @@ struct StaticAnalysisConfig
|
||||||
|
|
||||||
@INI("Checks that opEquals and toHash are both defined or neither are defined")
|
@INI("Checks that opEquals and toHash are both defined or neither are defined")
|
||||||
bool opequals_tohash_check;
|
bool opequals_tohash_check;
|
||||||
|
|
||||||
|
@INI("Checks for subtraction from .length properties")
|
||||||
|
bool length_subtraction_check;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright Brian Schott (Sir Alaran) 2014.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
module analysis.length_subtraction;
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
|
||||||
|
import std.d.ast;
|
||||||
|
import std.d.lexer;
|
||||||
|
import analysis.base;
|
||||||
|
import analysis.helpers;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for subtraction from a .length property. This is usually a bug.
|
||||||
|
*/
|
||||||
|
class LengthSubtractionCheck : BaseAnalyzer
|
||||||
|
{
|
||||||
|
alias visit = BaseAnalyzer.visit;
|
||||||
|
|
||||||
|
this(string fileName)
|
||||||
|
{
|
||||||
|
super(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const AddExpression addExpression)
|
||||||
|
{
|
||||||
|
if (addExpression.operator == tok!"-")
|
||||||
|
{
|
||||||
|
UnaryExpression l = cast(UnaryExpression) addExpression.left;
|
||||||
|
UnaryExpression r = cast(UnaryExpression) addExpression.right;
|
||||||
|
if (l is null || r is null)
|
||||||
|
{
|
||||||
|
// stderr.writeln(__FILE__, " ", __LINE__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (r.primaryExpression is null || r.primaryExpression.primary.type != tok!"intLiteral")
|
||||||
|
{
|
||||||
|
// stderr.writeln(__FILE__, " ", __LINE__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (l.identifierOrTemplateInstance is null
|
||||||
|
|| l.identifierOrTemplateInstance.identifier.text != "length")
|
||||||
|
{
|
||||||
|
// stderr.writeln(__FILE__, " ", __LINE__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
const(Token) token = l.identifierOrTemplateInstance.identifier;
|
||||||
|
addErrorMessage(token.line, token.column,
|
||||||
|
"Avoid subtracting from '.length' as it may be unsigned.");
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
addExpression.accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import analysis.config;
|
||||||
|
StaticAnalysisConfig sac;
|
||||||
|
sac.if_else_same_check = true;
|
||||||
|
assertAnalyzerWarnings(q{
|
||||||
|
void testSizeT()
|
||||||
|
{
|
||||||
|
if (i < a.length - 1) // [warn]: Avoid subtracting from '.length' as it may be unsigned.
|
||||||
|
writeln("something");
|
||||||
|
}
|
||||||
|
}c, sac);
|
||||||
|
stderr.writeln("Unittest for IfElseSameCheck passed.");
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import analysis.constructors;
|
||||||
import analysis.unused;
|
import analysis.unused;
|
||||||
import analysis.duplicate_attribute;
|
import analysis.duplicate_attribute;
|
||||||
import analysis.opequals_without_tohash;
|
import analysis.opequals_without_tohash;
|
||||||
|
import analysis.length_subtraction;
|
||||||
|
|
||||||
void messageFunction(string fileName, size_t line, size_t column, string message,
|
void messageFunction(string fileName, size_t line, size_t column, string message,
|
||||||
bool isError)
|
bool isError)
|
||||||
|
@ -94,6 +95,7 @@ string[] analyze(string fileName, ubyte[] code, StaticAnalysisConfig analysisCon
|
||||||
if (analysisConfig.unused_variable_check) checks ~= new UnusedVariableCheck(fileName);
|
if (analysisConfig.unused_variable_check) checks ~= new UnusedVariableCheck(fileName);
|
||||||
if (analysisConfig.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName);
|
if (analysisConfig.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName);
|
||||||
if (analysisConfig.opequals_tohash_check) checks ~= new OpEqualsWithoutToHashCheck(fileName);
|
if (analysisConfig.opequals_tohash_check) checks ~= new OpEqualsWithoutToHashCheck(fileName);
|
||||||
|
if (analysisConfig.length_subtraction_check) checks ~= new LengthSubtractionCheck(fileName);
|
||||||
|
|
||||||
foreach (check; checks)
|
foreach (check; checks)
|
||||||
{
|
{
|
||||||
|
|
28
astprinter.d
28
astprinter.d
|
@ -61,12 +61,12 @@ class XMLPrinter : ASTVisitor
|
||||||
{
|
{
|
||||||
output.writeln("<andAndExpression>");
|
output.writeln("<andAndExpression>");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
andAndExpression.left.accept(this);
|
visit(andAndExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
if (andAndExpression.right !is null)
|
if (andAndExpression.right !is null)
|
||||||
{
|
{
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
andAndExpression.right.accept(this);
|
visit(andAndExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
}
|
}
|
||||||
output.writeln("</andAndExpression>");
|
output.writeln("</andAndExpression>");
|
||||||
|
@ -76,12 +76,12 @@ class XMLPrinter : ASTVisitor
|
||||||
{
|
{
|
||||||
output.writeln("<andExpression>");
|
output.writeln("<andExpression>");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
andExpression.left.accept(this);
|
visit(andExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
if (andExpression.right !is null)
|
if (andExpression.right !is null)
|
||||||
{
|
{
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
andExpression.right.accept(this);
|
visit(andExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
}
|
}
|
||||||
output.writeln("</andExpression>");
|
output.writeln("</andExpression>");
|
||||||
|
@ -441,10 +441,10 @@ class XMLPrinter : ASTVisitor
|
||||||
{
|
{
|
||||||
output.writeln("<equalExpression operator=\"", str(equalExpression.operator), "\">");
|
output.writeln("<equalExpression operator=\"", str(equalExpression.operator), "\">");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
equalExpression.left.accept(this);
|
visit(equalExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
equalExpression.right.accept(this);
|
visit(equalExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
output.writeln("</equalExpression>");
|
output.writeln("</equalExpression>");
|
||||||
}
|
}
|
||||||
|
@ -956,12 +956,12 @@ class XMLPrinter : ASTVisitor
|
||||||
{
|
{
|
||||||
output.writeln("<powExpression>");
|
output.writeln("<powExpression>");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
powExpression.left.accept(this);
|
visit(powExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
if (powExpression.right !is null)
|
if (powExpression.right !is null)
|
||||||
{
|
{
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
powExpression.right.accept(this);
|
visit(powExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
}
|
}
|
||||||
output.writeln("</powExpression>");
|
output.writeln("</powExpression>");
|
||||||
|
@ -997,10 +997,10 @@ class XMLPrinter : ASTVisitor
|
||||||
output.writeln("<relExpression operator=\"",
|
output.writeln("<relExpression operator=\"",
|
||||||
xmlAttributeEscape(str(relExpression.operator)), "\">");
|
xmlAttributeEscape(str(relExpression.operator)), "\">");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
relExpression.left.accept(this);
|
visit(relExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
relExpression.right.accept(this);
|
visit(relExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
output.writeln("</relExpression>");
|
output.writeln("</relExpression>");
|
||||||
}
|
}
|
||||||
|
@ -1037,10 +1037,10 @@ class XMLPrinter : ASTVisitor
|
||||||
output.writeln("<shiftExpression operator=\"",
|
output.writeln("<shiftExpression operator=\"",
|
||||||
xmlAttributeEscape(str(shiftExpression.operator)), "\">");
|
xmlAttributeEscape(str(shiftExpression.operator)), "\">");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
shiftExpression.left.accept(this);
|
visit(shiftExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
shiftExpression.right.accept(this);
|
visit(shiftExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
output.writeln("</shiftExpression>");
|
output.writeln("</shiftExpression>");
|
||||||
}
|
}
|
||||||
|
@ -1496,12 +1496,12 @@ class XMLPrinter : ASTVisitor
|
||||||
{
|
{
|
||||||
output.writeln("<xorExpression>");
|
output.writeln("<xorExpression>");
|
||||||
output.writeln("<left>");
|
output.writeln("<left>");
|
||||||
xorExpression.left.accept(this);
|
visit(xorExpression.left);
|
||||||
output.writeln("</left>");
|
output.writeln("</left>");
|
||||||
if (xorExpression.right !is null)
|
if (xorExpression.right !is null)
|
||||||
{
|
{
|
||||||
output.writeln("<right>");
|
output.writeln("<right>");
|
||||||
xorExpression.right.accept(this);
|
visit(xorExpression.right);
|
||||||
output.writeln("</right>");
|
output.writeln("</right>");
|
||||||
}
|
}
|
||||||
output.writeln("</xorExpression>");
|
output.writeln("</xorExpression>");
|
||||||
|
|
Loading…
Reference in New Issue