This commit is contained in:
Hackerpilot 2016-01-27 00:16:50 -08:00
parent 2ec0259260
commit 1c96fddeb5
46 changed files with 540 additions and 499 deletions

View file

@ -52,8 +52,7 @@ unittest
add near ptr [EAX], 3;
}
}
}c, sac);
}c, );
stderr.writeln("Unittest for AsmStyleCheck passed.");
}

View file

@ -57,8 +57,7 @@ class AutoRefAssignmentCheck : BaseAnalyzer
if (ioti.identifier == tok!"" || interest <= 0)
return;
if (scopes[$ - 1].canFind(ioti.identifier.text))
addErrorMessage(ioti.identifier.line, ioti.identifier.column, KEY,
MESSAGE);
addErrorMessage(ioti.identifier.line, ioti.identifier.column, KEY, MESSAGE);
}
override void visit(const IdentifierChain ic)
@ -68,8 +67,7 @@ class AutoRefAssignmentCheck : BaseAnalyzer
if (ic.identifiers.length == 0 || interest <= 0)
return;
if (scopes[$ - 1].canFind(ic.identifiers[0].text))
addErrorMessage(ic.identifiers[0].line, ic.identifiers[0].column, KEY,
MESSAGE);
addErrorMessage(ic.identifiers[0].line, ic.identifiers[0].column, KEY, MESSAGE);
}
alias visit = BaseAnalyzer.visit;

View file

@ -67,4 +67,3 @@ protected:
MessageSet _messages;
}

View file

@ -47,14 +47,16 @@ class BuiltinPropertyNameCheck : BaseAnalyzer
override void visit(const FunctionBody functionBody)
{
immutable int d = depth;
scope(exit) depth = d;
scope (exit)
depth = d;
depth = 0;
functionBody.accept(this);
}
override void visit(const AutoDeclaration ad)
{
if (depth > 0) foreach (i; ad.identifiers)
if (depth > 0)
foreach (i; ad.identifiers)
{
if (isBuiltinProperty(i.text))
addErrorMessage(i.line, i.column, KEY, generateErrorMessage(i.text));
@ -81,6 +83,7 @@ private:
string generateErrorMessage(string name)
{
import std.string : format;
return format("Avoid naming members '%s'. This can"
~ " confuse code that depends on the '.%s' property of a type.", name, name);
}
@ -88,18 +91,18 @@ private:
bool isBuiltinProperty(string name)
{
import std.algorithm : canFind;
return builtinProperties.canFind(name);
}
enum string[] builtinProperties = [
"init", "sizeof", "mangleof", "alignof", "stringof"
];
enum string[] builtinProperties = ["init", "sizeof", "mangleof", "alignof", "stringof"];
int depth;
}
unittest
{
import analysis.config : StaticAnalysisConfig;
StaticAnalysisConfig sac;
sac.builtin_property_names_check = true;
assertAnalyzerWarnings(q{
@ -113,4 +116,3 @@ class SomeClass
stderr.writeln("Unittest for NumberStyleCheck passed.");
}

View file

@ -26,8 +26,7 @@ class CommaExpressionCheck : BaseAnalyzer
{
if (ex.items.length > 1 && interest > 0)
{
addErrorMessage(ex.line, ex.column, KEY,
"Avoid using the comma expression.");
addErrorMessage(ex.line, ex.column, KEY, "Avoid using the comma expression.");
}
ex.accept(this);
}

View file

@ -7,7 +7,6 @@ import analysis.base;
import analysis.helpers;
import dsymbol.scope_ : Scope;
class ConstructorCheck : BaseAnalyzer
{
alias visit = BaseAnalyzer.visit;
@ -74,7 +73,6 @@ class ConstructorCheck : BaseAnalyzer
}
}
private:
enum State : ubyte
@ -112,4 +110,3 @@ unittest
stderr.writeln("Unittest for ConstructorCheck passed.");
}

View file

@ -51,4 +51,3 @@ unittest
stderr.writeln("Unittest for DeleteCheck passed.");
}

View file

@ -13,7 +13,6 @@ import analysis.base;
import analysis.helpers;
import dsymbol.scope_ : Scope;
/**
* Checks for duplicate attributes such as @property, @safe,
* @trusted, @system, pure, and nothrow
@ -60,8 +59,7 @@ class DuplicateAttributeCheck : BaseAnalyzer
}
// Just return if missing function nodes
if (!node.functionDeclaration
|| !node.functionDeclaration.memberFunctionAttributes)
if (!node.functionDeclaration || !node.functionDeclaration.memberFunctionAttributes)
return;
// Check the functions
@ -82,7 +80,8 @@ class DuplicateAttributeCheck : BaseAnalyzer
}
}
void checkDuplicateAttribute(const string attributeName, const string attributeDesired, size_t line, size_t column, ref bool hasAttribute)
void checkDuplicateAttribute(const string attributeName,
const string attributeDesired, size_t line, size_t column, ref bool hasAttribute)
{
// Just return if not an attribute
if (attributeName != attributeDesired)
@ -102,9 +101,7 @@ class DuplicateAttributeCheck : BaseAnalyzer
string getAttributeName(const Attribute attribute, ref size_t line, ref size_t column)
{
// Get the name from the attribute identifier
if (attribute
&& attribute.atAttribute
&& attribute.atAttribute.identifier !is Token.init
if (attribute && attribute.atAttribute && attribute.atAttribute.identifier !is Token.init
&& attribute.atAttribute.identifier.text
&& attribute.atAttribute.identifier.text.length)
{
@ -125,11 +122,11 @@ class DuplicateAttributeCheck : BaseAnalyzer
return null;
}
string getAttributeName(const MemberFunctionAttribute memberFunctionAttribute, ref size_t line, ref size_t column)
string getAttributeName(const MemberFunctionAttribute memberFunctionAttribute,
ref size_t line, ref size_t column)
{
// Get the name from the tokenType
if (memberFunctionAttribute
&& memberFunctionAttribute.tokenType !is IdType.init
if (memberFunctionAttribute && memberFunctionAttribute.tokenType !is IdType.init
&& memberFunctionAttribute.tokenType.str
&& memberFunctionAttribute.tokenType.str.length)
{
@ -138,8 +135,7 @@ class DuplicateAttributeCheck : BaseAnalyzer
}
// Get the name from the attribute identifier
if (memberFunctionAttribute
&& memberFunctionAttribute.atAttribute
if (memberFunctionAttribute && memberFunctionAttribute.atAttribute
&& memberFunctionAttribute.atAttribute.identifier !is Token.init
&& memberFunctionAttribute.atAttribute.identifier.type == tok!"identifier"
&& memberFunctionAttribute.atAttribute.identifier.text
@ -158,6 +154,7 @@ class DuplicateAttributeCheck : BaseAnalyzer
unittest
{
import analysis.config : StaticAnalysisConfig;
StaticAnalysisConfig sac;
sac.duplicate_attribute = true;
assertAnalyzerWarnings(q{
@ -226,4 +223,3 @@ unittest
stderr.writeln("Unittest for DuplicateAttributeCheck passed.");
}

View file

@ -11,7 +11,9 @@ import analysis.base;
import std.algorithm : canFind;
import dsymbol.scope_ : Scope;
void doNothing(string, size_t, size_t, string, bool) {}
void doNothing(string, size_t, size_t, string, bool)
{
}
class EnumArrayLiteralCheck : BaseAnalyzer
{
@ -35,17 +37,19 @@ class EnumArrayLiteralCheck : BaseAnalyzer
{
foreach (i, initializer; autoDec.initializers)
{
if (initializer is null) continue;
if (initializer.nonVoidInitializer is null) continue;
if (initializer.nonVoidInitializer.arrayInitializer is null) continue;
addErrorMessage(autoDec.identifiers[i].line,
autoDec.identifiers[i].column, "dscanner.performance.enum_array_literal",
if (initializer is null)
continue;
if (initializer.nonVoidInitializer is null)
continue;
if (initializer.nonVoidInitializer.arrayInitializer is null)
continue;
addErrorMessage(autoDec.identifiers[i].line, autoDec.identifiers[i].column,
"dscanner.performance.enum_array_literal",
"This enum may lead to unnecessary allocation at run-time."
~ " Use 'static immutable " ~ autoDec.identifiers[i].text
~ " = [ ...' instead.");
~ " Use 'static immutable "
~ autoDec.identifiers[i].text ~ " = [ ...' instead.");
}
}
autoDec.accept(this);
}
}

View file

@ -28,14 +28,10 @@ class FloatOperatorCheck : BaseAnalyzer
override void visit(const RelExpression r)
{
if (r.operator == tok!"<>"
|| r.operator == tok!"<>="
|| r.operator == tok!"!<>"
|| r.operator == tok!"!>"
|| r.operator == tok!"!<"
|| r.operator == tok!"!<>="
|| r.operator == tok!"!>="
|| r.operator == tok!"!<=")
if (r.operator == tok!"<>" || r.operator == tok!"<>="
|| r.operator == tok!"!<>" || r.operator == tok!"!>"
|| r.operator == tok!"!<" || r.operator == tok!"!<>="
|| r.operator == tok!"!>=" || r.operator == tok!"!<=")
{
addErrorMessage(r.line, r.column, KEY,
"Avoid using the deprecated floating-point operators.");
@ -68,4 +64,3 @@ unittest
stderr.writeln("Unittest for FloatOperatorCheck passed.");
}

View file

@ -70,8 +70,7 @@ class FunctionAttributeCheck : BaseAnalyzer
foundProperty = foundProperty || (attribute.atAttribute !is null
&& attribute.atAttribute.identifier.text == "property");
foundConst = foundConst || attribute.tokenType == tok!"const"
|| attribute.tokenType == tok!"immutable"
|| attribute.tokenType == tok!"inout";
|| attribute.tokenType == tok!"immutable" || attribute.tokenType == tok!"inout";
}
if (foundProperty && !foundConst)
{
@ -93,21 +92,19 @@ class FunctionAttributeCheck : BaseAnalyzer
continue;
if (attr.attribute == tok!"abstract" && inInterface)
{
addErrorMessage(attr.attribute.line,
attr.attribute.column, KEY, ABSTRACT_MESSAGE);
addErrorMessage(attr.attribute.line, attr.attribute.column, KEY, ABSTRACT_MESSAGE);
continue;
}
if (dec.functionDeclaration !is null
&& (attr.attribute == tok!"const"
|| attr.attribute == tok!"inout"
|| attr.attribute == tok!"immutable"))
if (dec.functionDeclaration !is null && (attr.attribute == tok!"const"
|| attr.attribute == tok!"inout" || attr.attribute == tok!"immutable"))
{
import std.string : format;
immutable string attrString = str(attr.attribute.type);
addErrorMessage(dec.functionDeclaration.name.line,
dec.functionDeclaration.name.column, KEY,
format("'%s' is not an attribute of the return type."
~ " Place it after the parameter list to clarify.", attrString));
dec.functionDeclaration.name.column, KEY, format(
"'%s' is not an attribute of the return type." ~ " Place it after the parameter list to clarify.",
attrString));
}
}
end:

View file

@ -14,15 +14,12 @@ import analysis.config;
import analysis.run;
import analysis.base;
S between(S)(S value, S before, S after)
if (isSomeString!S)
S between(S)(S value, S before, S after) if (isSomeString!S)
{
return value.after(before).before(after);
}
S before(S)(S value, S separator)
if (isSomeString!S)
S before(S)(S value, S separator) if (isSomeString!S)
{
auto i = indexOf(value, separator);
@ -32,8 +29,7 @@ S before(S)(S value, S separator)
return value[0 .. i];
}
S after(S)(S value, S separator)
if (isSomeString!S)
S after(S)(S value, S separator) if (isSomeString!S)
{
auto i = indexOf(value, separator);
@ -50,7 +46,8 @@ S after(S)(S value, S separator)
* and make sure they match the warnings in the comments. Warnings are
* marked like so: // [warn]: Failed to do somethings.
*/
void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, string file=__FILE__, size_t line=__LINE__)
void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config,
string file = __FILE__, size_t line = __LINE__)
{
import analysis.run : ParseAllocator, parseModule;
import dparse.lexer : StringCache, Token;
@ -74,7 +71,8 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
immutable size_t rawLine = rawWarning.line;
if (rawLine == 0)
{
stderr.writefln("!!! Skipping warning because it is on line zero:\n%s", rawWarning.message);
stderr.writefln("!!! Skipping warning because it is on line zero:\n%s",
rawWarning.message);
continue;
}
@ -109,22 +107,15 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
// No warning
if (lineNo !in warnings)
{
immutable string errors = "Expected warning:\n%s\nFrom source code at (%s:?):\n%s".format(
messages[lineNo],
lineNo,
codeLines[lineNo - line]
);
immutable string errors = "Expected warning:\n%s\nFrom source code at (%s:?):\n%s".format(messages[lineNo],
lineNo, codeLines[lineNo - line]);
throw new core.exception.AssertError(errors, file, lineNo);
}
// Different warning
else if (warnings[lineNo] != messages[lineNo])
{
immutable string errors = "Expected warning:\n%s\nBut was:\n%s\nFrom source code at (%s:?):\n%s".format(
messages[lineNo],
warnings[lineNo],
lineNo,
codeLines[lineNo - line]
);
messages[lineNo], warnings[lineNo], lineNo, codeLines[lineNo - line]);
throw new core.exception.AssertError(errors, file, lineNo);
}
}
@ -136,11 +127,8 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
// Unexpected warning
if (lineNo !in messages)
{
unexpectedWarnings ~= "%s\nFrom source code at (%s:?):\n%s".format(
warning,
lineNo,
codeLines[lineNo - line]
);
unexpectedWarnings ~= "%s\nFrom source code at (%s:?):\n%s".format(warning,
lineNo, codeLines[lineNo - line]);
}
}
if (unexpectedWarnings.length)
@ -149,4 +137,3 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
throw new core.exception.AssertError(message, file, line);
}
}

View file

@ -29,8 +29,8 @@ class IfStatementCheck : BaseAnalyzer
if (ifStatement.expression.items.length == 1
&& (cast(AndAndExpression) ifStatement.expression.items[0]) is null)
{
redundancyCheck(ifStatement.expression, ifStatement.expression.line,
ifStatement.expression.column);
redundancyCheck(ifStatement.expression,
ifStatement.expression.line, ifStatement.expression.column);
}
inIfExpresson = true;
ifStatement.expression.accept(this);
@ -47,10 +47,8 @@ class IfStatementCheck : BaseAnalyzer
{
if (inIfExpresson)
{
redundancyCheck(andAndExpression, andAndExpression.line,
andAndExpression.column);
redundancyCheck(andAndExpression.left, andAndExpression.line,
andAndExpression.column);
redundancyCheck(andAndExpression, andAndExpression.line, andAndExpression.column);
redundancyCheck(andAndExpression.left, andAndExpression.line, andAndExpression.column);
redundancyCheck(andAndExpression.right, andAndExpression.line,
andAndExpression.column);
}
@ -68,8 +66,7 @@ private:
assert(depth >= 0);
}
void redundancyCheck(const ExpressionNode expression, size_t line,
size_t column)
void redundancyCheck(const ExpressionNode expression, size_t line, size_t column)
{
import std.string : format;
import std.array : appender;
@ -82,10 +79,8 @@ private:
immutable size_t prevLocation = alreadyChecked(app.data, line, column);
if (prevLocation != size_t.max)
{
addErrorMessage(line, column, KEY,
"Expression %s is true: already checked on line %d.".format(
expressions[prevLocation].formatted,
expressions[prevLocation].line));
addErrorMessage(line, column, KEY, "Expression %s is true: already checked on line %d.".format(
expressions[prevLocation].formatted, expressions[prevLocation].line));
}
else
{

View file

@ -33,19 +33,18 @@ class IfElseSameCheck : BaseAnalyzer
{
if (ifStatement.thenStatement == ifStatement.elseStatement)
addErrorMessage(ifStatement.line, ifStatement.column,
"dscanner.bugs.if_else_same",
"'Else' branch is identical to 'Then' branch.");
"dscanner.bugs.if_else_same", "'Else' branch is identical to 'Then' branch.");
ifStatement.accept(this);
}
override void visit(const AssignExpression assignExpression)
{
auto e = cast(const AssignExpression) (cast(const Expression) assignExpression.expression).items[$ - 1];
auto e = cast(const AssignExpression)(cast(const Expression) assignExpression.expression)
.items[$ - 1];
if (e !is null && assignExpression.operator == tok!"="
&& e.ternaryExpression == assignExpression.ternaryExpression)
{
addErrorMessage(assignExpression.line, assignExpression.column,
"dscanner.bugs.self_assignment",
addErrorMessage(assignExpression.line, assignExpression.column, "dscanner.bugs.self_assignment",
"Left side of assignment operatior is identical to the right side.");
}
assignExpression.accept(this);
@ -99,4 +98,3 @@ unittest
}c, sac);
stderr.writeln("Unittest for IfElseSameCheck passed.");
}

View file

@ -86,8 +86,7 @@ private:
{
const(Thing)* thing = name.text in s;
if (thing is null)
currentScope[name.text] = Thing(name.text, name.line, name.column,
!fromLabel/+, isConditional+/);
currentScope[name.text] = Thing(name.text, name.line, name.column, !fromLabel /+, isConditional+/ );
else if (i != 0 || !isConditional)
{
immutable thisKind = fromLabel ? "Label" : "Variable";

View file

@ -13,7 +13,6 @@ import analysis.base;
import analysis.helpers;
import dsymbol.scope_;
/**
* Checks for subtraction from a .length property. This is usually a bug.
*/
@ -49,8 +48,7 @@ class LengthSubtractionCheck : BaseAnalyzer
goto end;
}
const(Token) token = l.identifierOrTemplateInstance.identifier;
addErrorMessage(token.line, token.column,
"dscanner.suspicious.length_subtraction",
addErrorMessage(token.line, token.column, "dscanner.suspicious.length_subtraction",
"Avoid subtracting from '.length' as it may be unsigned.");
}
end:
@ -73,4 +71,3 @@ unittest
}c, sac);
stderr.writeln("Unittest for IfElseSameCheck passed.");
}

View file

@ -49,11 +49,12 @@ class LocalImportCheck : BaseAnalyzer
override void visit(const ImportDeclaration id)
{
if ((!isStatic && interesting) && (id.importBindings is null || id.importBindings.importBinds.length == 0))
if ((!isStatic && interesting) && (id.importBindings is null
|| id.importBindings.importBinds.length == 0))
{
addErrorMessage(id.singleImports[0].identifierChain.identifiers[0].line,
id.singleImports[0].identifierChain.identifiers[0].column,
"dscanner.suspicious.local_imports", "Local imports should specify"
id.singleImports[0].identifierChain.identifiers[0].column, "dscanner.suspicious.local_imports",
"Local imports should specify"
~ " the symbols being imported to avoid hiding local symbols.");
}
}
@ -74,4 +75,3 @@ private:
bool interesting;
bool isStatic;
}

View file

@ -32,11 +32,14 @@ class LogicPrecedenceCheck : BaseAnalyzer
override void visit(const OrOrExpression orOr)
{
if (orOr.left is null || orOr.right is null) return;
if (orOr.left is null || orOr.right is null)
return;
const AndAndExpression left = cast(AndAndExpression) orOr.left;
const AndAndExpression right = cast(AndAndExpression) orOr.right;
if (left is null && right is null) return;
if ((left !is null && left.right is null) && (right !is null && right.right is null)) return;
if (left is null && right is null)
return;
if ((left !is null && left.right is null) && (right !is null && right.right is null))
return;
addErrorMessage(orOr.line, orOr.column, KEY,
"Use parenthesis to clarify this expression.");
orOr.accept(this);
@ -60,4 +63,3 @@ unittest
}c, sac);
stderr.writeln("Unittest for LogicPrecedenceCheck passed.");
}

View file

@ -31,8 +31,8 @@ final class MismatchedArgumentCheck : BaseAnalyzer
auto identVisitor = scoped!IdentVisitor;
identVisitor.visit(fce.unaryExpression);
const(DSymbol)*[] symbols = resolveSymbol(sc,
identVisitor.names.length > 0 ? identVisitor.names : [CONSTRUCTOR_SYMBOL_NAME]);
const(DSymbol)*[] symbols = resolveSymbol(sc, identVisitor.names.length > 0
? identVisitor.names : [CONSTRUCTOR_SYMBOL_NAME]);
static struct ErrorMessage
{
@ -56,13 +56,14 @@ final class MismatchedArgumentCheck : BaseAnalyzer
{
foreach (size_t i, ref const mm; mismatches)
{
messages ~= ErrorMessage(argVisitor.lines[i], argVisitor.columns[i],
createWarningFromMismatch(mm));
messages ~= ErrorMessage(argVisitor.lines[i],
argVisitor.columns[i], createWarningFromMismatch(mm));
}
}
}
if (!matched) foreach (m; messages)
if (!matched)
foreach (m; messages)
addErrorMessage(m.line, m.column, KEY, m.message);
}

View file

@ -32,14 +32,16 @@ public:
override void visit(const Token t)
{
import std.algorithm : startsWith;
if (isNumberLiteral(t.type) && !t.text.startsWith("0x")
&& ((t.text.startsWith("0b") && !t.text.matchFirst(badBinaryRegex).empty)
|| !t.text.matchFirst(badDecimalRegex).empty))
&& ((t.text.startsWith("0b") && !t.text.matchFirst(badBinaryRegex)
.empty) || !t.text.matchFirst(badDecimalRegex).empty))
{
addErrorMessage(t.line, t.column, "dscanner.style.number_literals",
"Use underscores to improve number constant readability.");
}
}
private:
auto badBinaryRegex = ctRegex!(`^0b[01]{9,}`);
auto badDecimalRegex = ctRegex!(`^\d{5,}`);
@ -48,6 +50,7 @@ private:
unittest
{
import analysis.config : StaticAnalysisConfig;
StaticAnalysisConfig sac;
sac.number_style_check = true;
assertAnalyzerWarnings(q{
@ -66,4 +69,3 @@ unittest
stderr.writeln("Unittest for NumberStyleCheck passed.");
}

View file

@ -34,8 +34,7 @@ class ObjectConstCheck : BaseAnalyzer
override void visit(const Declaration d)
{
if (inAggregate && d.functionDeclaration !is null
&& isInteresting(d.functionDeclaration.name.text)
&& (!hasConst(d.attributes)
&& isInteresting(d.functionDeclaration.name.text) && (!hasConst(d.attributes)
&& !hasConst(d.functionDeclaration.memberFunctionAttributes)))
{
addErrorMessage(d.functionDeclaration.name.line,
@ -48,15 +47,16 @@ class ObjectConstCheck : BaseAnalyzer
private static bool hasConst(const Attribute[] attributes)
{
import std.algorithm : any;
return attributes.any!(a => a.attribute == tok!"const");
}
private static bool hasConst(const MemberFunctionAttribute[] attributes)
{
import std.algorithm : any;
return attributes.any!(a => a.tokenType == tok!"const"
|| a.tokenType == tok!"immutable"
|| a.tokenType == tok!"inout");
|| a.tokenType == tok!"immutable" || a.tokenType == tok!"inout");
}
private static bool isInteresting(string name)
@ -72,6 +72,7 @@ class ObjectConstCheck : BaseAnalyzer
unittest
{
import analysis.config : StaticAnalysisConfig;
StaticAnalysisConfig sac;
sac.object_const_check = true;
assertAnalyzerWarnings(q{
@ -129,4 +130,3 @@ unittest
stderr.writeln("Unittest for ObjectConstCheck passed.");
}

View file

@ -44,17 +44,14 @@ class OpEqualsWithoutToHashCheck : BaseAnalyzer
bool hasOpCmp = false;
// Just return if missing children
if (!structBody
|| !structBody.declarations
|| name is Token.init)
if (!structBody || !structBody.declarations || name is Token.init)
return;
// Check all the function declarations
foreach (declaration; structBody.declarations)
{
// Skip if not a function declaration
if (!declaration
|| !declaration.functionDeclaration)
if (!declaration || !declaration.functionDeclaration)
continue;
// Check if opEquals or toHash
@ -150,4 +147,3 @@ unittest
stderr.writeln("Unittest for OpEqualsWithoutToHashCheck passed.");
}

View file

@ -4,4 +4,3 @@ public import analysis.style;
public import analysis.enumarrayliteral;
public import analysis.pokemon;
public import analysis.base;

View file

@ -12,7 +12,6 @@ import analysis.base;
import analysis.helpers;
import dsymbol.scope_ : Scope;
/**
* Checks for Pokémon exception handling, i.e. "gotta' catch 'em all".
*
@ -53,10 +52,10 @@ class PokemonExceptionCheck : BaseAnalyzer
c.accept(this);
}
override void visit(const Type2 type2)
{
if (ignoreType) return;
if (ignoreType)
return;
if (type2.type !is null)
{
@ -68,7 +67,8 @@ class PokemonExceptionCheck : BaseAnalyzer
{
return;
}
auto identOrTemplate = type2.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances[0];
auto identOrTemplate = type2.symbol.identifierOrTemplateChain
.identifiersOrTemplateInstances[0];
if (identOrTemplate.templateInstance !is null)
{
return;
@ -129,4 +129,3 @@ unittest
stderr.writeln("Unittest for PokemonExceptionCheck passed.");
}

View file

@ -37,6 +37,7 @@ class BackwardsRangeCheck : BaseAnalyzer
if (foreachStatement.low !is null && foreachStatement.high !is null)
{
import std.string : format;
state = State.left;
visit(foreachStatement.low);
state = State.right;
@ -82,14 +83,18 @@ class BackwardsRangeCheck : BaseAnalyzer
line = primary.primary.line;
this.column = primary.primary.column;
try left = parseNumber(primary.primary.text);
catch (ConvException e) return;
try
left = parseNumber(primary.primary.text);
catch (ConvException e)
return;
hasLeft = true;
}
else
{
try right = parseNumber(primary.primary.text);
catch (ConvException e) return;
try
right = parseNumber(primary.primary.text);
catch (ConvException e)
return;
hasRight = true;
}
}
@ -106,8 +111,8 @@ class BackwardsRangeCheck : BaseAnalyzer
if (hasLeft && hasRight && left > right)
{
import std.string : format;
string message = format(
"%d is larger than %d. This slice is likely incorrect.",
string message = format("%d is larger than %d. This slice is likely incorrect.",
left, right);
addErrorMessage(line, this.column, KEY, message);
}
@ -124,13 +129,20 @@ private:
long right;
size_t column;
size_t line;
enum State { ignore, left, right }
enum State
{
ignore,
left,
right
}
State state = State.ignore;
long parseNumber(string te)
{
import std.conv : to;
import std.string : removechars;
string t = te.removechars("_uUlL");
if (t.length > 2)
{
@ -167,4 +179,3 @@ unittest
stderr.writeln("Unittest for BackwardsRangeCheck passed.");
}

View file

@ -71,12 +71,10 @@ private alias ASTAllocator = CAllocatorImpl!(
void messageFunction(string fileName, size_t line, size_t column, string message, bool isError)
{
writefln("%s(%d:%d)[%s]: %s", fileName, line, column, isError ? "error" : "warn",
message);
writefln("%s(%d:%d)[%s]: %s", fileName, line, column, isError ? "error" : "warn", message);
}
void messageFunctionJSON(string fileName, size_t line, size_t column, string message,
bool)
void messageFunctionJSON(string fileName, size_t line, size_t column, string message, bool)
{
writeJSON("dscanner.syntax", fileName, line, column, message);
}
@ -160,8 +158,8 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config,
uint errorCount = 0;
uint warningCount = 0;
const(Token)[] tokens;
const Module m = parseModule(fileName, code, p, cache, false, tokens, null,
&errorCount, &warningCount);
const Module m = parseModule(fileName, code, p, cache, false, tokens,
null, &errorCount, &warningCount);
assert(m);
if (errorCount > 0 || (staticAnalyze && warningCount > 0))
hasErrors = true;
@ -187,13 +185,12 @@ const(Module) parseModule(string fileName, ubyte[] code, ParseAllocator p,
tokens = getTokensForParser(code, config, &cache);
if (linesOfCode !is null)
(*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens);
return dparse.parser.parseModule(tokens, fileName, p,
report ? &messageFunctionJSON : &messageFunction, errorCount, warningCount);
return dparse.parser.parseModule(tokens, fileName, p, report
? &messageFunctionJSON : &messageFunction, errorCount, warningCount);
}
MessageSet analyze(string fileName, const Module m,
const StaticAnalysisConfig analysisConfig, ref ModuleCache moduleCache,
const(Token)[] tokens, bool staticAnalyze = true)
MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig analysisConfig,
ref ModuleCache moduleCache, const(Token)[] tokens, bool staticAnalyze = true)
{
if (!staticAnalyze)
return null;

View file

@ -35,8 +35,8 @@ class StyleChecker : BaseAnalyzer
foreach (part; dec.moduleName.identifiers)
{
if (part.text.matchFirst(moduleNameRegex).length == 0)
addErrorMessage(part.line, part.column, KEY, "Module/package name '"
~ part.text ~ "' does not match style guidelines.");
addErrorMessage(part.line, part.column, KEY,
"Module/package name '" ~ part.text ~ "' does not match style guidelines.");
}
}
@ -53,8 +53,8 @@ class StyleChecker : BaseAnalyzer
void checkLowercaseName(string type, ref const Token name)
{
if (name.text.length > 0 && name.text.matchFirst(varFunNameRegex).length == 0)
addErrorMessage(name.line, name.column, KEY, type ~ " name '"
~ name.text ~ "' does not match style guidelines.");
addErrorMessage(name.line, name.column, KEY,
type ~ " name '" ~ name.text ~ "' does not match style guidelines.");
}
override void visit(const ClassDeclaration dec)
@ -86,8 +86,8 @@ class StyleChecker : BaseAnalyzer
void checkAggregateName(string aggregateType, ref const Token name)
{
if (name.text.length > 0 && name.text.matchFirst(aggregateNameRegex).length == 0)
addErrorMessage(name.line, name.column, KEY, aggregateType
~ " name '" ~ name.text ~ "' does not match style guidelines.");
addErrorMessage(name.line, name.column, KEY,
aggregateType ~ " name '" ~ name.text ~ "' does not match style guidelines.");
}
}
@ -114,4 +114,3 @@ unittest
stderr.writeln("Unittest for StyleChecker passed.");
}

View file

@ -71,7 +71,8 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
ovr = true;
else if (attribute.deprecated_ !is null)
dep = true;
else if (attribute.atAttribute !is null && attribute.atAttribute.identifier.text == "disable")
else if (attribute.atAttribute !is null
&& attribute.atAttribute.identifier.text == "disable")
dis = true;
}
if (ovr)
@ -120,9 +121,17 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
visit(f);
}
override void visit(const FunctionBody fb) {}
override void visit(const Unittest u) {}
override void visit(const TraitsExpression t) {}
override void visit(const FunctionBody fb)
{
}
override void visit(const Unittest u)
{
}
override void visit(const TraitsExpression t)
{
}
mixin V!ClassDeclaration;
mixin V!InterfaceDeclaration;
@ -139,6 +148,7 @@ private:
override void visit(const T declaration)
{
import std.traits : hasMember;
if (currentIsInteresting())
{
if (declaration.comment.ptr is null)
@ -148,19 +158,20 @@ private:
static if (is(T == FunctionDeclaration))
{
import std.algorithm : canFind;
if (!(ignoredFunctionNames.canFind(declaration.name.text)
|| isGetterOrSetter(declaration.name.text)
|| isProperty(declaration)))
{
addMessage(declaration.name.line, declaration.name.column,
declaration.name.text);
addMessage(declaration.name.line,
declaration.name.column, declaration.name.text);
}
}
else
{
if (declaration.name.type != tok!"")
addMessage(declaration.name.line, declaration.name.column,
declaration.name.text);
addMessage(declaration.name.line,
declaration.name.column, declaration.name.text);
}
}
else
@ -168,8 +179,7 @@ private:
addMessage(declaration.line, declaration.column, null);
}
}
static if (!(is (T == TemplateDeclaration)
|| is(T == FunctionDeclaration)))
static if (!(is(T == TemplateDeclaration) || is(T == FunctionDeclaration)))
{
declaration.accept(this);
}
@ -197,9 +207,10 @@ private:
void addMessage(size_t line, size_t column, string name)
{
import std.string : format;
addErrorMessage(line, column, "dscanner.style.undocumented_declaration",
name is null ? "Public declaration is undocumented." :
format("Public declaration '%s' is undocumented.", name));
addErrorMessage(line, column, "dscanner.style.undocumented_declaration", name is null
? "Public declaration is undocumented."
: format("Public declaration '%s' is undocumented.", name));
}
bool getOverride()
@ -234,19 +245,25 @@ private:
bool currentIsInteresting()
{
return stack[$ - 1].protection == tok!"public" && !stack[$ - 1].isOverride
&& !stack[$ - 1].isDisabled && !stack[$ - 1].isDeprecated;
return stack[$ - 1].protection == tok!"public"
&& !stack[$ - 1].isOverride && !stack[$ - 1].isDisabled && !stack[$ - 1].isDeprecated;
}
void set(IdType p)
in { assert (isProtection(p)); }
in
{
assert(isProtection(p));
}
body
{
stack[$ - 1].protection = p;
}
void push(IdType p)
in { assert (isProtection(p)); }
in
{
assert(isProtection(p));
}
body
{
stack ~= ProtectionInfo(p, false);
@ -271,11 +288,7 @@ private:
// Ignore undocumented symbols with these names
private immutable string[] ignoredFunctionNames = [
"opCmp",
"opEquals",
"toString",
"toHash",
"main"
"opCmp", "opEquals", "toString", "toHash", "main"
];
private enum getSetRe = ctRegex!`^(?:get|set)(?:\p{Lu}|_).*`;

View file

@ -77,8 +77,7 @@ class UnmodifiedFinder : BaseAnalyzer
{
if (initializedFromCast(autoDeclaration.initializers[i]))
continue;
tree[$ - 1].insert(new VariableInfo(id.text, id.line,
id.column));
tree[$ - 1].insert(new VariableInfo(id.text, id.line, id.column));
}
}
autoDeclaration.accept(this);
@ -222,6 +221,7 @@ private:
foundCast = true;
castExpression.accept(this);
}
bool foundCast = false;
}
@ -235,14 +235,15 @@ private:
bool canFindImmutableOrConst(const Declaration dec)
{
import std.algorithm : canFind, map, filter;
return !dec.attributes.map!(a => a.attribute).filter!(
a => a == cast(IdType) tok!"immutable" || a == cast(IdType) tok!"const")
.empty;
return !dec.attributes.map!(a => a.attribute)
.filter!(a => a == cast(IdType) tok!"immutable" || a == cast(IdType) tok!"const").empty;
}
bool canFindImmutable(const VariableDeclaration dec)
{
import std.algorithm : canFind;
foreach (storageClass; dec.storageClasses)
{
if (storageClass.token == tok!"enum")
@ -274,10 +275,8 @@ private:
foreach (vi; tree[$ - 1])
{
immutable string errorMessage = "Variable " ~ vi.name
~ " is never modified and could have been declared const"
~ " or immutable.";
addErrorMessage(vi.line, vi.column, "dscanner.suspicious.unmodified",
errorMessage);
~ " is never modified and could have been declared const" ~ " or immutable.";
addErrorMessage(vi.line, vi.column, "dscanner.suspicious.unmodified", errorMessage);
}
tree = tree[0 .. $ - 1];
}

View file

@ -37,7 +37,8 @@ class UnusedVariableCheck : BaseAnalyzer
override void visit(const Declaration declaration)
{
if (!isOverride) foreach (attribute; declaration.attributes)
if (!isOverride)
foreach (attribute; declaration.attributes)
isOverride = isOverride || (attribute.attribute == tok!"override");
declaration.accept(this);
isOverride = false;
@ -275,11 +276,13 @@ class UnusedVariableCheck : BaseAnalyzer
{
import std.algorithm : canFind;
import std.array : array;
if (parameter.name != tok!"")
{
immutable bool isRef = canFind(parameter.parameterAttributes, cast(IdType) tok!"ref")
|| canFind(parameter.parameterAttributes, cast(IdType) tok!"in")
|| canFind(parameter.parameterAttributes, cast(IdType) tok!"out");
|| canFind(parameter.parameterAttributes,
cast(IdType) tok!"in") || canFind(parameter.parameterAttributes,
cast(IdType) tok!"out");
variableDeclared(parameter.name.text, parameter.name.line,
parameter.name.column, true, isRef);
if (parameter.default_ !is null)
@ -338,8 +341,7 @@ private:
}
}
void variableDeclared(string name, size_t line, size_t column,
bool isParameter, bool isRef)
void variableDeclared(string name, size_t line, size_t column, bool isParameter, bool isRef)
{
if (inAggregateScope)
return;
@ -364,11 +366,11 @@ private:
{
if (!uu.isRef && tree.length > 1)
{
immutable string certainty = uu.uncertain ? " might not be used." : " is never used.";
immutable string certainty = uu.uncertain ? " might not be used."
: " is never used.";
immutable string errorMessage = (uu.isParameter ? "Parameter " : "Variable ")
~ uu.name ~ certainty;
addErrorMessage(uu.line, uu.column,
uu.isParameter ? "dscanner.suspicious.unused_parameter"
addErrorMessage(uu.line, uu.column, uu.isParameter ? "dscanner.suspicious.unused_parameter"
: "dscanner.suspicious.unused_variable", errorMessage);
}
}

View file

@ -76,11 +76,13 @@ class UnusedLabelCheck : BaseAnalyzer
if (contStatement.label.text.length)
labelUsed(contStatement.label.text);
}
override void visit(const BreakStatement breakStatement)
{
if (breakStatement.label.text.length)
labelUsed(breakStatement.label.text);
}
override void visit(const GotoStatement gotoStatement)
{
if (gotoStatement.label.text.length)
@ -116,8 +118,7 @@ private:
assert(label.line != size_t.max && label.column != size_t.max);
if (!label.used)
{
addErrorMessage(label.line, label.column,
"dscanner.suspicious.unused_label",
addErrorMessage(label.line, label.column, "dscanner.suspicious.unused_label",
"Label \"" ~ label.name ~ "\" is not used.");
}
}

View file

@ -116,4 +116,3 @@ unittest
.format(UselessAssertCheck.MESSAGE), sac);
stderr.writeln("Unittest for UselessAssertCheck passed.");
}

View file

@ -84,7 +84,8 @@ class XMLPrinter : ASTVisitor
}
if (asmInstruction.asmInstruction !is null)
{
output.writeln("<label label=\"", asmInstruction.identifierOrIntegerOrOpcode.text, "\"/>");
output.writeln("<label label=\"",
asmInstruction.identifierOrIntegerOrOpcode.text, "\"/>");
asmInstruction.asmInstruction.accept(this);
}
else if (asmInstruction.identifierOrIntegerOrOpcode != tok!"")
@ -244,8 +245,7 @@ class XMLPrinter : ASTVisitor
if (continueStatement.label.type == tok!"")
output.writeln("<continueStatement/>");
else
output.writeln("<continueStatement label=\"",
continueStatement.label.text, "\"/>");
output.writeln("<continueStatement label=\"", continueStatement.label.text, "\"/>");
}
override void visit(const DebugCondition debugCondition)
@ -365,8 +365,7 @@ class XMLPrinter : ASTVisitor
override void visit(const ForeachStatement foreachStatement)
{
output.writeln("<foreachStatement type=\"", str(
foreachStatement.type), "\">");
output.writeln("<foreachStatement type=\"", str(foreachStatement.type), "\">");
if (foreachStatement.foreachType !is null)
visit(foreachStatement.foreachType);
if (foreachStatement.foreachTypeList !is null)
@ -413,10 +412,8 @@ class XMLPrinter : ASTVisitor
override void visit(const FunctionLiteralExpression functionLiteralExpression)
{
output.writeln("<functionLiteralExpression type=\"",
functionLiteralExpression.functionOrDelegate != tok!""
? str(functionLiteralExpression.functionOrDelegate)
: "auto", "\">");
output.writeln("<functionLiteralExpression type=\"", functionLiteralExpression.functionOrDelegate != tok!""
? str(functionLiteralExpression.functionOrDelegate) : "auto", "\">");
functionLiteralExpression.accept(this);
output.writeln("</functionLiteralExpression>");
}
@ -580,8 +577,7 @@ class XMLPrinter : ASTVisitor
override void visit(const LabeledStatement labeledStatement)
{
output.writeln("<labeledStatement label=\"",
labeledStatement.identifier.text ,"\">");
output.writeln("<labeledStatement label=\"", labeledStatement.identifier.text, "\">");
visit(labeledStatement.declarationOrStatement);
output.writeln("</labeledStatement>");
}
@ -669,7 +665,6 @@ class XMLPrinter : ASTVisitor
output.writeln("</parameter>");
}
override void visit(const PowExpression powExpression)
{
output.writeln("<powExpression>");
@ -780,15 +775,15 @@ class XMLPrinter : ASTVisitor
override void visit(const TemplateDeclaration templateDeclaration)
{
writeDdoc(templateDeclaration.comment);
output.writeln("<templateDeclaration line=\"",
templateDeclaration.name.line, "\">");
output.writeln("<templateDeclaration line=\"", templateDeclaration.name.line, "\">");
writeName(templateDeclaration.name.text);
visit(templateDeclaration.templateParameters);
if (templateDeclaration.constraint !is null)
visit(templateDeclaration.constraint);
foreach (dec; templateDeclaration.declarations)
{
if (dec !is null) visit(dec);
if (dec !is null)
visit(dec);
}
output.writeln("</templateDeclaration>");
}
@ -798,26 +793,65 @@ class XMLPrinter : ASTVisitor
string tagName;
switch (token.type)
{
case tok!"": return;
case tok!"identifier": tagName = "identifier"; break;
case tok!"doubleLiteral": tagName = "doubleLiteral"; break;
case tok!"idoubleLiteral": tagName = "idoubleLiteral"; break;
case tok!"floatLiteral": tagName = "floatLiteral"; break;
case tok!"ifloatLiteral": tagName = "ifloatLiteral"; break;
case tok!"intLiteral": tagName = "intLiteral"; break;
case tok!"uintLiteral": tagName = "uintLiteral"; break;
case tok!"longLiteral": tagName = "longLiteral"; break;
case tok!"ulongLiteral": tagName = "ulongLiteral"; break;
case tok!"realLiteral": tagName = "realLiteral"; break;
case tok!"irealLiteral": tagName = "irealLiteral"; break;
case tok!"characterLiteral": tagName = "characterLiteral"; break;
case tok!"stringLiteral": tagName = "stringLiteral"; break;
case tok!"dstringLiteral": tagName = "dstringLiteral"; break;
case tok!"wstringLiteral": tagName = "wstringLiteral"; break;
case tok!"scriptLine": tagName = "scriptLine"; break;
case tok!"$": output.writeln("<dollar/>"); return;
case tok!".": output.writeln("<dot/>"); return;
default: output.writeln("<", str(token.type), "/>"); return;
case tok!"":
return;
case tok!"identifier":
tagName = "identifier";
break;
case tok!"doubleLiteral":
tagName = "doubleLiteral";
break;
case tok!"idoubleLiteral":
tagName = "idoubleLiteral";
break;
case tok!"floatLiteral":
tagName = "floatLiteral";
break;
case tok!"ifloatLiteral":
tagName = "ifloatLiteral";
break;
case tok!"intLiteral":
tagName = "intLiteral";
break;
case tok!"uintLiteral":
tagName = "uintLiteral";
break;
case tok!"longLiteral":
tagName = "longLiteral";
break;
case tok!"ulongLiteral":
tagName = "ulongLiteral";
break;
case tok!"realLiteral":
tagName = "realLiteral";
break;
case tok!"irealLiteral":
tagName = "irealLiteral";
break;
case tok!"characterLiteral":
tagName = "characterLiteral";
break;
case tok!"stringLiteral":
tagName = "stringLiteral";
break;
case tok!"dstringLiteral":
tagName = "dstringLiteral";
break;
case tok!"wstringLiteral":
tagName = "wstringLiteral";
break;
case tok!"scriptLine":
tagName = "scriptLine";
break;
case tok!"$":
output.writeln("<dollar/>");
return;
case tok!".":
output.writeln("<dot/>");
return;
default:
output.writeln("<", str(token.type), "/>");
return;
}
output.writeln("<", tagName, ">", xmlEscape(token.text), "</", tagName, ">");
}
@ -884,7 +918,8 @@ class XMLPrinter : ASTVisitor
visit(typeSuffix.parameters);
foreach (attr; typeSuffix.memberFunctionAttributes)
{
if (attr !is null) visit(attr);
if (attr !is null)
visit(attr);
}
}
}
@ -894,8 +929,7 @@ class XMLPrinter : ASTVisitor
output.writeln("<unaryExpression>");
if (unaryExpression.prefix != tok!"")
{
output.writeln("<prefix>", xmlEscape(str(unaryExpression.prefix.type)),
"</prefix>");
output.writeln("<prefix>", xmlEscape(str(unaryExpression.prefix.type)), "</prefix>");
unaryExpression.unaryExpression.accept(this);
}
else
@ -904,8 +938,7 @@ class XMLPrinter : ASTVisitor
{
assert(unaryExpression.suffix.text == "");
unaryExpression.unaryExpression.accept(this);
output.writeln("<suffix>", str(unaryExpression.suffix.type),
"</suffix>");
output.writeln("<suffix>", str(unaryExpression.suffix.type), "</suffix>");
}
else
unaryExpression.accept(this);
@ -975,6 +1008,7 @@ class XMLPrinter : ASTVisitor
output.writeln("</index>");
}
// dfmt off
override void visit(const AliasInitializer aliasInitializer) { mixin (tagAndAccept!"aliasInitializer"); }
override void visit(const AliasThisDeclaration aliasThisDeclaration) { mixin (tagAndAccept!"aliasThisDeclaration"); }
override void visit(const AnonymousEnumDeclaration anonymousEnumDeclaration) { mixin (tagAndAccept!"anonymousEnumDeclaration"); }
@ -1095,6 +1129,7 @@ class XMLPrinter : ASTVisitor
override void visit(const VersionSpecification versionSpecification) { mixin (tagAndAccept!"versionSpecification"); }
override void visit(const WhileStatement whileStatement) { mixin (tagAndAccept!"whileStatement"); }
override void visit(const WithStatement withStatement) { mixin (tagAndAccept!"withStatement"); } override void visit(const TypeidExpression typeidExpression) { mixin (tagAndAccept!"typeidExpression"); }
// dfmt on
alias visit = ASTVisitor.visit;
@ -1105,8 +1140,8 @@ class XMLPrinter : ASTVisitor
private static string xmlAttributeEscape(string s)
{
return s.translate(['<' : "&lt;", '>' : "&gt;", '&' : "&amp;",
'\"' : "&quot;", '\'' : "&apos;"]);
return s.translate(['<' : "&lt;", '>' : "&gt;", '&' : "&amp;", '\"'
: "&quot;", '\'' : "&apos;"]);
}
private void writeName(string name)
@ -1116,7 +1151,8 @@ class XMLPrinter : ASTVisitor
private void writeDdoc(string comment)
{
if (comment.ptr is null) return;
if (comment.ptr is null)
return;
output.writeln("<ddoc>", xmlEscape(comment), "</ddoc>");
}
@ -1130,7 +1166,6 @@ private:
template tagAndAccept(string tagName)
{
immutable tagAndAccept = `output.writeln("<` ~ tagName ~ `>");`
~ tagName ~ `.accept(this);`
~ `output.writeln("</` ~ tagName ~ `>");`;
immutable tagAndAccept = `output.writeln("<` ~ tagName ~ `>");` ~ tagName
~ `.accept(this);` ~ `output.writeln("</` ~ tagName ~ `>");`;
}

View file

@ -42,8 +42,8 @@ void printCtags(File output, string[] fileNames)
printer.fileName = fileName;
printer.visit(m);
}
output.write(
"!_TAG_FILE_FORMAT\t2\n" ~ "!_TAG_FILE_SORTED\t1\n" ~ "!_TAG_FILE_AUTHOR\tBrian Schott\n" ~ "!_TAG_PROGRAM_URL\thttps://github.com/Hackerpilot/Dscanner/\n");
output.write("!_TAG_FILE_FORMAT\t2\n" ~ "!_TAG_FILE_SORTED\t1\n" ~ "!_TAG_FILE_AUTHOR\tBrian Schott\n"
~ "!_TAG_PROGRAM_URL\thttps://github.com/Hackerpilot/Dscanner/\n");
tags[].copy(output.lockingTextWriter);
}
@ -131,9 +131,8 @@ final class CTagsPrinter: ASTVisitor
{
auto params = paramsToString(dec);
tagLines.insert("%s\t%s\t%d;\"\tT\tline:%d%s%s\tsignature:%s\n".format(
dec.name.text, fileName, dec.name.line, dec.name.line, context.c,
context.access, params));
tagLines.insert("%s\t%s\t%d;\"\tT\tline:%d%s%s\tsignature:%s\n".format(dec.name.text,
fileName, dec.name.line, dec.name.line, context.c, context.access, params));
immutable c = context;
context = ContextType("\ttemplate:" ~ dec.name.text, context.access);
dec.accept(this);
@ -144,9 +143,8 @@ final class CTagsPrinter: ASTVisitor
{
auto params = paramsToString(dec);
tagLines.insert("%s\t%s\t%d;\"\tf\tline:%d%s%s\tsignature:%s\n".format(
dec.name.text, fileName, dec.name.line, dec.name.line, context.c,
context.access, params));
tagLines.insert("%s\t%s\t%d;\"\tf\tline:%d%s%s\tsignature:%s\n".format(dec.name.text,
fileName, dec.name.line, dec.name.line, context.c, context.access, params));
}
override void visit(const Constructor dec)

View file

@ -10,8 +10,12 @@ module dscanner_version;
*/
enum DSCANNER_VERSION = "v0.3.0-alpha";
version (Windows) {}
else version (built_with_dub) {}
version (Windows)
{
}
else version (built_with_dub)
{
}
else
{
/**

View file

@ -40,7 +40,8 @@ void printEtags(File output, bool tagAll, string[] fileNames)
foreach (fileName; fileNames)
{
File f = File(fileName);
if (f.size == 0) continue;
if (f.size == 0)
continue;
auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(bytes);
auto tokens = getTokensForParser(bytes, config, &cache);
@ -50,9 +51,7 @@ void printEtags(File output, bool tagAll, string[] fileNames)
printer.moduleName = m.moduleFullName(fileName);
version (UseModuleContext)
printer.context = printer.moduleName ~ ".";
printer.privateVisibility = tagAll?
Visibility.exposed :
Visibility.hidden;
printer.privateVisibility = tagAll ? Visibility.exposed : Visibility.hidden;
printer.bytes = bytes.sansBOM;
printer.visit(m);
@ -63,15 +62,20 @@ void printEtags(File output, bool tagAll, string[] fileNames)
private:
enum Visibility {exposed, hidden}
enum Visibility
{
exposed,
hidden
}
void doNothing(string, size_t, size_t, string, bool) {}
void doNothing(string, size_t, size_t, string, bool)
{
}
ubyte[] sansBOM(ubyte[] bytes)
{
// At least handle UTF-8 since there is some in druntime/phobos
return (bytes.length >= 3 &&
bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
return (bytes.length >= 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
? bytes[3 .. $] : bytes;
}
@ -109,7 +113,8 @@ final class EtagsPrinter : ASTVisitor
// visibility needs to be restored to what it was when changed by
// attribute.
auto was = visibility;
foreach (attr; dec.attributes) {
foreach (attr; dec.attributes)
{
updateVisibility(attr.attribute.type);
}
@ -256,7 +261,8 @@ final class EtagsPrinter : ASTVisitor
}
private:
void updateVisibility(IdType type) {
void updateVisibility(IdType type)
{
// maybe change visibility based on attribute 'type'
switch (type)
{
@ -292,25 +298,24 @@ private:
void maketag(string text, size_t index, ulong line)
{
// skip unittests and hidden declarations
if (inUnittest || visibility == Visibility.hidden) return;
if (inUnittest || visibility == Visibility.hidden)
return;
// tag is a searchable string from beginning of line
size_t b = index;
while (b > 0 && bytes[b-1] != '\n') --b;
while (b > 0 && bytes[b - 1] != '\n')
--b;
// tag end is one char beyond tag name
size_t e = index + text.length;
if (e < bytes.length && bytes[e] != '\n') ++e;
if (e < bytes.length && bytes[e] != '\n')
++e;
auto tag = cast(char[]) bytes[b .. e];
auto tagname = context.empty ? text : context ~ text;
// drum roll... the etags tag format
tags ~= format("%s\x7f%s\x01%u,%u\n",
tag,
tagname,
line,
b);
tags ~= format("%s\x7f%s\x01%u,%u\n", tag, tagname, line, b);
}
alias visit = ASTVisitor.visit;

View file

@ -70,8 +70,9 @@ html { background-color: #fdf6e3; color: #002b36; }
void writeSpan(string cssClass, string value)
{
version (Windows)
stdout.write(`<span class="`, cssClass, `">`, value.replace("&", "&amp;").replace("<", "&lt;").replace("\r", ""), `</span>`);
stdout.write(`<span class="`, cssClass, `">`, value.replace("&",
"&amp;").replace("<", "&lt;").replace("\r", ""), `</span>`);
else
stdout.write(`<span class="`, cssClass, `">`, value.replace("&", "&amp;").replace("<", "&lt;"), `</span>`);
stdout.write(`<span class="`, cssClass, `">`, value.replace("&",
"&amp;").replace("<", "&lt;"), `</span>`);
}

View file

@ -28,7 +28,8 @@ class ImportPrinter : ASTVisitor
override void visit(const IdentifierChain identifierChain)
{
if (ignore) return;
if (ignore)
return;
bool first = true;
string s;
foreach (ident; identifierChain.identifiers)

View file

@ -34,7 +34,9 @@ import inifiled;
import dsymbol.modulecache;
version (unittest)
void main() {}
void main()
{
}
else
int main(string[] args)
{
@ -62,15 +64,30 @@ int main(string[] args)
try
{
getopt(args, std.getopt.config.caseSensitive, "sloc|l", &sloc,
"highlight", &highlight, "ctags|c", &ctags, "help|h", &help,
"etags|e", &etags, "etagsAll", &etagsAll,
"tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast, "imports|i", &imports, "outline|o", &outline,
"tokenDump", &tokenDump, "styleCheck|S", &styleCheck,
"defaultConfig", &defaultConfig, "declaration|d", &symbolName,
"config", &configLocation, "report", &report, "I", &importPaths,
"version", &printVersion, "muffinButton", &muffin, "explore", &explore);
// dfmt off
getopt(args, std.getopt.config.caseSensitive,
"sloc|l", &sloc,
"highlight", &highlight,
"ctags|c", &ctags,
"help|h", &help,
"etags|e", &etags,
"etagsAll", &etagsAll,
"tokenCount|t", &tokenCount,
"syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast,
"imports|i", &imports,
"outline|o", &outline,
"tokenDump", &tokenDump,
"styleCheck|S", &styleCheck,
"defaultConfig", &defaultConfig,
"declaration|d", &symbolName,
"config", &configLocation,
"report", &report,
"I", &importPaths,
"version", &printVersion,
"muffinButton", &muffin,
"explore", &explore);
//dfmt on
}
catch (ConvException e)
{
@ -80,8 +97,7 @@ int main(string[] args)
if (muffin)
{
stdout.writeln(
` ___________
stdout.writeln(` ___________
__(#*O 0** @%*)__
_(%*o#*O%*0 #O#%##@)_
(*#@%#o*@ #o%O*%@ #o #)
@ -117,17 +133,17 @@ int main(string[] args)
return 0;
}
const(string[]) absImportPaths = importPaths.map!(
a => a.absolutePath().buildNormalizedPath()).array();
const(string[]) absImportPaths = importPaths.map!(a => a.absolutePath()
.buildNormalizedPath()).array();
auto moduleCache = ModuleCache(new dsymbol.modulecache.ASTAllocator);
if (absImportPaths.length)
moduleCache.addImportPaths(absImportPaths);
immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck, ast, imports, outline, tokenDump, styleCheck, defaultConfig,
report, symbolName !is null, etags, etagsAll]);
immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount, syntaxCheck, ast, imports,
outline, tokenDump, styleCheck, defaultConfig, report,
symbolName !is null, etags, etagsAll]);
if (optionCount > 1)
{
stderr.writeln("Too many options specified");
@ -140,7 +156,8 @@ int main(string[] args)
}
// --report implies --styleCheck
if (report) styleCheck = true;
if (report)
styleCheck = true;
StringCache cache = StringCache(StringCache.defaultBucketCount);
if (defaultConfig)
@ -167,11 +184,13 @@ int main(string[] args)
else if (tokenDump)
{
auto tokens = getTokensForParser(bytes, config, &cache);
writeln("text blank\tindex\tline\tcolumn\ttype\tcomment\ttrailingComment");
writeln(
"text blank\tindex\tline\tcolumn\ttype\tcomment\ttrailingComment");
foreach (token; tokens)
{
writefln("<<%20s>>%b\t%d\t%d\t%d\t%d\t%s", token.text is null ? str(token.type) : token.text,
token.text !is null, token.index, token.line, token.column, token.type, token.comment);
writefln("<<%20s>>%b\t%d\t%d\t%d\t%d\t%s", token.text is null
? str(token.type) : token.text, token.text !is null, token.index,
token.line, token.column, token.type, token.comment);
}
return 0;
}
@ -244,9 +263,8 @@ int main(string[] args)
foreach (name; expandArgs(fileNames))
{
config.fileName = name;
auto tokens = getTokensForParser(
usingStdin ? readStdin() : readFile(name),
config, &cache);
auto tokens = getTokensForParser(usingStdin ? readStdin()
: readFile(name), config, &cache);
auto mod = parseModule(tokens, name, null, &doNothing);
visitor.visit(mod);
}
@ -259,9 +277,8 @@ int main(string[] args)
LexerConfig config;
config.fileName = fileName;
config.stringBehavior = StringBehavior.source;
auto tokens = getTokensForParser(
usingStdin ? readStdin() : readFile(args[1]),
config, &cache);
auto tokens = getTokensForParser(usingStdin ? readStdin()
: readFile(args[1]), config, &cache);
auto mod = parseModule(tokens, fileName, null, &doNothing);
if (ast)
@ -280,21 +297,16 @@ int main(string[] args)
return 0;
}
string[] expandArgs(string[] args)
{
// isFile can throw if it's a broken symlink.
bool isFileSafe(T)(T a)
{
try
{
return isFile(a);
}
catch (FileException)
{
return false;
}
}
string[] rVal;
if (args.length == 1)
@ -303,7 +315,8 @@ string[] expandArgs(string[] args)
{
if (isFileSafe(arg))
rVal ~= arg;
else foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
else
foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
{
if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`)))
rVal ~= item;
@ -336,7 +349,8 @@ ubyte[] readFile(string fileName)
return [];
}
File f = File(fileName);
if (f.size == 0) return [];
if (f.size == 0)
return [];
ubyte[] sourceCode = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(sourceCode);
return sourceCode;
@ -344,8 +358,7 @@ ubyte[] readFile(string fileName)
void printHelp(string programName)
{
stderr.writefln(
`
stderr.writefln(`
Usage: %s <options>
Options:
@ -420,7 +433,9 @@ Options:
programName);
}
private void doNothing(string, size_t, size_t, string, bool) {}
private void doNothing(string, size_t, size_t, string, bool)
{
}
private enum CONFIG_FILE_NAME = "dscanner.ini";
version (linux) version = useXDG;
@ -436,6 +451,7 @@ string getConfigurationLocation()
version (useXDG)
{
import std.process : environment;
string configDir = environment.get("XDG_CONFIG_HOME", null);
if (configDir is null)
{
@ -445,13 +461,9 @@ string getConfigurationLocation()
configDir = buildPath(configDir, ".config", "dscanner", CONFIG_FILE_NAME);
}
else
{
configDir = buildPath(configDir, "dscanner", CONFIG_FILE_NAME);
}
return configDir;
}
else version (Windows)
{
return CONFIG_FILE_NAME;
}
}

View file

@ -83,8 +83,7 @@ class Outliner : ASTVisitor
override void visit(const InterfaceDeclaration interfaceDec)
{
printIndentation();
output.writeln("interface ", interfaceDec.name.text, " : ",
interfaceDec.name.line);
output.writeln("interface ", interfaceDec.name.text, " : ", interfaceDec.name.line);
indent();
interfaceDec.accept(this);
outdent();
@ -94,8 +93,7 @@ class Outliner : ASTVisitor
override void visit(const StructDeclaration structDec)
{
printIndentation();
output.writeln("struct ", structDec.name.text, " : ",
structDec.name.line);
output.writeln("struct ", structDec.name.text, " : ", structDec.name.line);
indent();
structDec.accept(this);
outdent();
@ -113,18 +111,19 @@ class Outliner : ASTVisitor
finish();
}
//dfmt off
override void visit(const StaticConstructor s) {}
override void visit(const StaticDestructor s) {}
override void visit(const SharedStaticConstructor s) {}
override void visit(const SharedStaticDestructor s) {}
override void visit(const Constructor c) {}
override void visit(const Unittest u) {}
// dfmt on
override void visit(const UnionDeclaration unionDeclaration)
{
printIndentation();
output.writeln("union ", unionDeclaration.name.text, " : ",
unionDeclaration.name.line);
output.writeln("union ", unionDeclaration.name.text, " : ", unionDeclaration.name.line);
indent();
unionDeclaration.accept(this);
outdent();

View file

@ -52,4 +52,3 @@ ulong printLineCount(Tokens)(File output, string fileName, ref Tokens tokens)
output.writefln("%s:\t%d", fileName, count);
return count;
}

View file

@ -16,6 +16,7 @@ void findDeclarationOf(File output, string symbolName, string[] fileNames)
{
import std.array : uninitializedArray, array;
import std.conv : to;
LexerConfig config;
StringCache cache = StringCache(StringCache.defaultBucketCount);
auto visitor = new FinderVisitor(output, symbolName);
@ -23,7 +24,8 @@ void findDeclarationOf(File output, string symbolName, string[] fileNames)
{
File f = File(fileName);
assert(isFile(fileName));
if (f.size == 0) continue;
if (f.size == 0)
continue;
auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(bytes);
auto tokens = getTokensForParser(bytes, config, &cache);
@ -35,7 +37,9 @@ void findDeclarationOf(File output, string symbolName, string[] fileNames)
private:
void doNothing(string, size_t, size_t, string, bool) {}
void doNothing(string, size_t, size_t, string, bool)
{
}
class FinderVisitor : ASTVisitor
{
@ -103,7 +107,9 @@ class FinderVisitor : ASTVisitor
}
}
override void visit(const FunctionBody) {}
override void visit(const FunctionBody)
{
}
mixin template generateVisit(T)
{