mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-05-14 14:56:03 +03:00
Run dfmt
This commit is contained in:
parent
2ec0259260
commit
1c96fddeb5
46 changed files with 540 additions and 499 deletions
|
@ -52,8 +52,7 @@ unittest
|
|||
add near ptr [EAX], 3;
|
||||
}
|
||||
}
|
||||
}c, sac);
|
||||
}c, );
|
||||
|
||||
stderr.writeln("Unittest for AsmStyleCheck passed.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -67,4 +67,3 @@ protected:
|
|||
|
||||
MessageSet _messages;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -51,4 +51,3 @@ unittest
|
|||
|
||||
stderr.writeln("Unittest for DeleteCheck passed.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -4,4 +4,3 @@ public import analysis.style;
|
|||
public import analysis.enumarrayliteral;
|
||||
public import analysis.pokemon;
|
||||
public import analysis.base;
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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}|_).*`;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,4 +116,3 @@ unittest
|
|||
.format(UselessAssertCheck.MESSAGE), sac);
|
||||
stderr.writeln("Unittest for UselessAssertCheck passed.");
|
||||
}
|
||||
|
||||
|
|
127
src/astprinter.d
127
src/astprinter.d
|
@ -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(['<' : "<", '>' : ">", '&' : "&",
|
||||
'\"' : """, '\'' : "'"]);
|
||||
return s.translate(['<' : "<", '>' : ">", '&' : "&", '\"'
|
||||
: """, '\'' : "'"]);
|
||||
}
|
||||
|
||||
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 ~ `>");`;
|
||||
}
|
||||
|
|
14
src/ctags.d
14
src/ctags.d
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/**
|
||||
|
|
41
src/etags.d
41
src/etags.d
|
@ -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;
|
||||
|
|
|
@ -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("&", "&").replace("<", "<").replace("\r", ""), `</span>`);
|
||||
stdout.write(`<span class="`, cssClass, `">`, value.replace("&",
|
||||
"&").replace("<", "<").replace("\r", ""), `</span>`);
|
||||
else
|
||||
stdout.write(`<span class="`, cssClass, `">`, value.replace("&", "&").replace("<", "<"), `</span>`);
|
||||
stdout.write(`<span class="`, cssClass, `">`, value.replace("&",
|
||||
"&").replace("<", "<"), `</span>`);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
94
src/main.d
94
src/main.d
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -52,4 +52,3 @@ ulong printLineCount(Tokens)(File output, string fileName, ref Tokens tokens)
|
|||
output.writefln("%s:\t%d", fileName, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue