Added basic style checker

This commit is contained in:
Hackerpilot 2014-01-12 15:39:45 +00:00
parent 070f9ac83b
commit 22ca0cd284
3 changed files with 110 additions and 2 deletions

View File

@ -8,6 +8,7 @@ dmd\
astprinter.d\ astprinter.d\
formatter.d\ formatter.d\
outliner.d\ outliner.d\
style.d\
stdx/*.d\ stdx/*.d\
stdx/d/*.d\ stdx/d/*.d\
datapicked/dpick/buffer/*.d\ datapicked/dpick/buffer/*.d\

15
main.d
View File

@ -25,6 +25,7 @@ import ctags;
import astprinter; import astprinter;
import imports; import imports;
import outliner; import outliner;
import style;
int main(string[] args) int main(string[] args)
{ {
@ -41,6 +42,7 @@ int main(string[] args)
bool muffin; bool muffin;
bool outline; bool outline;
bool tokenDump; bool tokenDump;
bool styleCheck;
try try
{ {
@ -48,7 +50,8 @@ int main(string[] args)
"ctags|c", &ctags, "recursive|r|R", &recursive, "help|h", &help, "ctags|c", &ctags, "recursive|r|R", &recursive, "help|h", &help,
"tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck, "tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast, "imports|i", &imports, "outline|o", &outline, "ast|xml", &ast, "imports|i", &imports, "outline|o", &outline,
"tokenDump", &tokenDump, "muffinButton", &muffin); "tokenDump", &tokenDump, "styleCheck", &styleCheck,
"muffinButton", &muffin);
} }
catch (Exception e) catch (Exception e)
{ {
@ -77,7 +80,7 @@ int main(string[] args)
} }
auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount, auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck, ast, imports, outline, tokenDump]); syntaxCheck, ast, imports, outline, tokenDump, styleCheck]);
if (optionCount > 1) if (optionCount > 1)
{ {
stderr.writeln("Too many options specified"); stderr.writeln("Too many options specified");
@ -112,6 +115,10 @@ int main(string[] args)
{ {
stdout.printCtags(expandArgs(args, recursive)); stdout.printCtags(expandArgs(args, recursive));
} }
else if (styleCheck)
{
stdout.styleCheck(expandArgs(args, recursive));
}
else else
{ {
bool usingStdin = args.length == 1; bool usingStdin = args.length == 1;
@ -251,6 +258,10 @@ options:
syntax errors to stdout. One error or warning is printed per line. syntax errors to stdout. One error or warning is printed per line.
If no files are specified, input is read from stdin. If no files are specified, input is read from stdin.
--styleCheck [sourceFiles]
Lexes and parses sourceFiles, printing the line and column number of any
style guideline violations to stdout.
--ctags | -c sourceFile --ctags | -c sourceFile
Generates ctags information from the given source code file. Note that Generates ctags information from the given source code file. Note that
ctags information requires a filename, so stdin cannot be used in place ctags information requires a filename, so stdin cannot be used in place

96
style.d Normal file
View File

@ -0,0 +1,96 @@
// Copyright Brian Schott (Sir Alaran) 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module style;
import stdx.d.ast;
import stdx.d.lexer;
import stdx.d.parser;
import std.stdio;
import std.regex;
import std.array;
import std.conv;
void doNothing(string, size_t, size_t, string) {}
void styleCheck(File output, string[] fileNames)
{
foreach (fileName; fileNames)
{
File f = File(fileName);
auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(bytes);
auto tokens = byToken(bytes);
Module m = parseModule(tokens.array, fileName, &doNothing);
auto checker = new StyleChecker;
checker.fileName = fileName;
checker.visit(m);
}
}
class StyleChecker : ASTVisitor
{
enum varFunNameRegex = `^([\p{Ll}_][_\w\d]*|[\p{Lu}_]+)$`;
enum aggregateNameRegex = `^\p{Lu}[\w\d]*$`;
enum moduleNameRegex = `^\p{Ll}+$`;
override void visit(ModuleDeclaration dec)
{
foreach (part; dec.moduleName.identifiers)
{
if (part.text.matchFirst(moduleNameRegex).length == 0)
writeln(fileName, "(", part.line, ":", part.column, ") ",
"Module/package name ", part.text, " does not match style guidelines");
}
}
override void visit(Declarator dec)
{
checkLowercaseName("Variable", dec.name);
}
override void visit(FunctionDeclaration dec)
{
checkLowercaseName("Function", dec.name);
}
void checkLowercaseName(string type, ref Token name)
{
if (name.text.matchFirst(varFunNameRegex).length == 0)
writeln(fileName, "(", name.line, ":", name.column, ") ",
type, " name ", name.text, " does not match style guidelines");
}
override void visit(ClassDeclaration dec)
{
checkAggregateName("Class", dec.name);
dec.accept(this);
}
override void visit(EnumDeclaration dec)
{
if (dec.name.text is null || dec.name.text.length == 0)
return;
checkAggregateName("Enum", dec.name);
dec.accept(this);
}
override void visit(StructDeclaration dec)
{
checkAggregateName("Struct", dec.name);
dec.accept(this);
}
void checkAggregateName(string aggregateType, ref Token name)
{
if (name.text.matchFirst(aggregateNameRegex).length == 0)
writeln(fileName, "(", name.line, ":", name.column, ") ",
aggregateType, " name ", name.text,
" does not match style guidelines");
}
alias ASTVisitor.visit visit;
string fileName;
}