This commit is contained in:
Hackerpilot 2014-07-08 15:00:00 -07:00
parent 9f6688a306
commit 53cff7824e
21 changed files with 222 additions and 76 deletions

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = libdparse path = libdparse
url = https://github.com/Hackerpilot/libdparse.git url = https://github.com/Hackerpilot/libdparse.git
branch = master branch = master
[submodule "inifiled"]
path = inifiled
url = https://github.com/burner/inifiled.git

59
analysis/config.d Normal file
View File

@ -0,0 +1,59 @@
// Copyright Brian Schott (Hackerpilot) 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module analysis.config;
import inifiled;
StaticAnalysisConfig defaultStaticAnalysisConfig()
{
StaticAnalysisConfig config;
foreach (mem; __traits(allMembers, StaticAnalysisConfig))
mixin ("config." ~ mem ~ " = true;");
return config;
}
@INI("Configurue which static analysis checks are enabled")
struct StaticAnalysisConfig
{
@INI("Check variable, class, struct, interface, union, and function names against the Phobos style guide")
bool style_check;
@INI("Check for array literals that cause unnecessary allocation")
bool enum_array_literal_check;
@INI("Check for poor exception handling practices")
bool exception_check;
@INI("Check for use of the deprecated 'delete' keyword")
bool delete_check;
@INI("Check for use of the deprecated floating point operators")
bool float_operator_check;
@INI("Check number literals for readability")
bool number_style_check;
@INI("Checks that opEquals, opCmp, toHash, and toString are either const, immutable, or inout.")
bool object_const_check;
@INI("Checks for .. expressions where the left side is larger than the right.")
bool backwards_range_check;
@INI("Checks for if statements whose 'then' block is the same as the 'else' block")
bool if_else_same_check;
@INI("Checks for some problems with constructors")
bool constructor_check;
@INI("Checks for unused variables and function parameters")
bool unused_variable_check;
@INI("Checks for duplicate attributes")
bool duplicate_attribute;
@INI("Checks that opEquals and toHash are both defined or neither are defined")
bool opequals_tohash_check;
}

View File

@ -90,6 +90,9 @@ private:
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.constructor_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing. class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing.
{ {
@ -102,7 +105,7 @@ unittest
this() {} this() {}
this(string name = "doggie") {} // [warn]: This struct constructor can never be called with its default argument. this(string name = "doggie") {} // [warn]: This struct constructor can never be called with its default argument.
} }
}c, analysis.run.AnalyzerCheck.constructor_check); }c, sac);
stderr.writeln("Unittest for ConstructorCheck passed."); stderr.writeln("Unittest for ConstructorCheck passed.");
} }

View File

@ -1,4 +1,4 @@
// Copyright Brian Schott (Sir Alaran) 2014. // Copyright Brian Schott (Hackerpilot) 2014.
// Distributed under the Boost Software License, Version 1.0. // Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at // (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt) // http://www.boost.org/LICENSE_1_0.txt)
@ -32,6 +32,9 @@ class DeleteCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.delete_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testDelete() void testDelete()
{ {
@ -41,7 +44,7 @@ unittest
auto a = new Class(); auto a = new Class();
delete a; // [warn]: Avoid using the 'delete' keyword. delete a; // [warn]: Avoid using the 'delete' keyword.
} }
}c, analysis.run.AnalyzerCheck.delete_check); }c, sac);
stderr.writeln("Unittest for DeleteCheck passed."); stderr.writeln("Unittest for DeleteCheck passed.");
} }

View File

@ -160,6 +160,9 @@ class DuplicateAttributeCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.duplicate_attribute = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
class ExampleAttributes class ExampleAttributes
{ {
@ -222,7 +225,7 @@ unittest
return false; return false;
} }
} }
}c, analysis.run.AnalyzerCheck.duplicate_attribute); }c, sac);
stderr.writeln("Unittest for DuplicateAttributeCheck passed."); stderr.writeln("Unittest for DuplicateAttributeCheck passed.");
} }

View File

@ -42,6 +42,9 @@ class FloatOperatorCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.float_operator_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testFish() void testFish()
{ {
@ -56,7 +59,7 @@ unittest
a = z !< z; // [warn]: Avoid using the deprecated floating-point operators. a = z !< z; // [warn]: Avoid using the deprecated floating-point operators.
a = z !<= z; // [warn]: Avoid using the deprecated floating-point operators. a = z !<= z; // [warn]: Avoid using the deprecated floating-point operators.
} }
}c, analysis.run.AnalyzerCheck.float_operator_check); }c, sac);
stderr.writeln("Unittest for FloatOperatorCheck passed."); stderr.writeln("Unittest for FloatOperatorCheck passed.");
} }

View File

@ -10,10 +10,11 @@ import std.traits;
import std.stdio; import std.stdio;
import std.d.ast; import std.d.ast;
import analysis.config;
import analysis.run; import analysis.run;
S between(S)(S value, S before, S after) S between(S)(S value, S before, S after)
if (isSomeString!S) if (isSomeString!S)
{ {
return value.after(before).before(after); return value.after(before).before(after);
@ -48,12 +49,12 @@ S after(S)(S value, S separator)
* and make sure they match the warnings in the comments. Warnings are * and make sure they match the warnings in the comments. Warnings are
* marked like so: // [warn]: Failed to do somethings. * marked like so: // [warn]: Failed to do somethings.
*/ */
void assertAnalyzerWarnings(string code, analysis.run.AnalyzerCheck analyzers, string file=__FILE__, size_t line=__LINE__) void assertAnalyzerWarnings(string code, StaticAnalysisConfig config, string file=__FILE__, size_t line=__LINE__)
{ {
import analysis.run; import analysis.run;
// Run the code and get any warnings // Run the code and get any warnings
string[] rawWarnings = analyze("test", cast(ubyte[]) code, analyzers); string[] rawWarnings = analyze("test", cast(ubyte[]) code, config);
string[] codeLines = code.split("\n"); string[] codeLines = code.split("\n");
// Get the warnings ordered by line // Get the warnings ordered by line

View File

@ -48,6 +48,9 @@ class IfElseSameCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.if_else_same_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testSizeT() void testSizeT()
{ {
@ -62,7 +65,7 @@ unittest
else else
person = "bobby"; // not same person = "bobby"; // not same
} }
}c, analysis.run.AnalyzerCheck.if_else_same_check); }c, sac);
stderr.writeln("Unittest for IfElseSameCheck passed."); stderr.writeln("Unittest for IfElseSameCheck passed.");
} }

View File

@ -42,6 +42,9 @@ class NumberStyleCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.number_style_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testNumbers() void testNumbers()
{ {
@ -54,7 +57,7 @@ unittest
a = 100000; // [warn]: Use underscores to improve number constant readability. a = 100000; // [warn]: Use underscores to improve number constant readability.
a = 1000000; // [warn]: Use underscores to improve number constant readability. a = 1000000; // [warn]: Use underscores to improve number constant readability.
} }
}c, analysis.run.AnalyzerCheck.number_style_check); }c, sac);
stderr.writeln("Unittest for NumberStyleCheck passed."); stderr.writeln("Unittest for NumberStyleCheck passed.");
} }

View File

@ -71,6 +71,9 @@ class ObjectConstCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.object_const_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testConsts() void testConsts()
{ {
@ -122,7 +125,7 @@ unittest
} }
} }
} }
}c, analysis.run.AnalyzerCheck.object_const_check); }c, sac);
stderr.writeln("Unittest for ObjectConstCheck passed."); stderr.writeln("Unittest for ObjectConstCheck passed.");
} }

View File

@ -80,6 +80,9 @@ class OpEqualsWithoutToHashCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.opequals_tohash_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
// Success because it has opEquals and toHash // Success because it has opEquals and toHash
class Chimp class Chimp
@ -130,7 +133,7 @@ unittest
return 0; return 0;
} }
} }
}c, analysis.run.AnalyzerCheck.opequals_tohash_check); }c, sac);
stderr.writeln("Unittest for OpEqualsWithoutToHashCheck passed."); stderr.writeln("Unittest for OpEqualsWithoutToHashCheck passed.");
} }

View File

@ -66,6 +66,9 @@ class PokemonExceptionCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.exception_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testCatch() void testCatch()
{ {
@ -93,7 +96,7 @@ unittest
{ {
} }
} }
}c, analysis.run.AnalyzerCheck.exception_check); }c, sac);
stderr.writeln("Unittest for PokemonExceptionCheck passed."); stderr.writeln("Unittest for PokemonExceptionCheck passed.");
} }

View File

@ -131,6 +131,9 @@ class BackwardsRangeCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.backwards_range_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testRange() void testRange()
{ {
@ -142,7 +145,7 @@ unittest
foreach (n; 1 .. 3) { } // ok foreach (n; 1 .. 3) { } // ok
foreach (n; 3 .. 1) { } // [warn]: 3 is larger than 1. Did you mean to use 'foreach_reverse( ... ; 1 .. 3)'? foreach (n; 3 .. 1) { } // [warn]: 3 is larger than 1. Did you mean to use 'foreach_reverse( ... ; 1 .. 3)'?
} }
}c, analysis.run.AnalyzerCheck.backwards_range_check); }c, sac);
stderr.writeln("Unittest for BackwardsRangeCheck passed."); stderr.writeln("Unittest for BackwardsRangeCheck passed.");
} }

View File

@ -10,6 +10,7 @@ import std.d.lexer;
import std.d.parser; import std.d.parser;
import std.d.ast; import std.d.ast;
import analysis.config;
import analysis.base; import analysis.base;
import analysis.style; import analysis.style;
import analysis.enumarrayliteral; import analysis.enumarrayliteral;
@ -25,25 +26,6 @@ import analysis.unused;
import analysis.duplicate_attribute; import analysis.duplicate_attribute;
import analysis.opequals_without_tohash; import analysis.opequals_without_tohash;
enum AnalyzerCheck : uint
{
none = 0b00000000_00000000,
style_check = 0b00000000_00000001,
enum_array_literal_check = 0b00000000_00000010,
exception_check = 0b00000000_00000100,
delete_check = 0b00000000_00001000,
float_operator_check = 0b00000000_00010000,
number_style_check = 0b00000000_00100000,
object_const_check = 0b00000000_01000000,
backwards_range_check = 0b00000000_10000000,
if_else_same_check = 0b00000001_00000000,
constructor_check = 0b00000010_00000000,
unused_variable_check = 0b00000100_00000000,
duplicate_attribute = 0b00001000_00000000,
opequals_tohash_check = 0b00010000_00000000,
all = 0b11111111_11111111
}
void messageFunction(string fileName, size_t line, size_t column, string message, void messageFunction(string fileName, size_t line, size_t column, string message,
bool isError) bool isError)
{ {
@ -53,11 +35,12 @@ void messageFunction(string fileName, size_t line, size_t column, string message
void syntaxCheck(File output, string[] fileNames) void syntaxCheck(File output, string[] fileNames)
{ {
analyze(output, fileNames, AnalyzerCheck.all, false); StaticAnalysisConfig config = defaultStaticAnalysisConfig();
analyze(output, fileNames, config, false);
} }
// For multiple files // For multiple files
void analyze(File output, string[] fileNames, AnalyzerCheck analyzers, bool staticAnalyze = true) void analyze(File output, string[] fileNames, StaticAnalysisConfig config, bool staticAnalyze = true)
{ {
foreach (fileName; fileNames) foreach (fileName; fileNames)
{ {
@ -66,14 +49,14 @@ void analyze(File output, string[] fileNames, AnalyzerCheck analyzers, bool stat
auto code = uninitializedArray!(ubyte[])(to!size_t(f.size)); auto code = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(code); f.rawRead(code);
string[] results = analyze(fileName, code, analyzers, staticAnalyze); string[] results = analyze(fileName, code, config, staticAnalyze);
if (results.length > 0) if (results.length > 0)
output.writeln(results.join("\n")); output.writeln(results.join("\n"));
} }
} }
// For a string // For a string
string[] analyze(string fileName, ubyte[] code, AnalyzerCheck analyzers, bool staticAnalyze = true) string[] analyze(string fileName, ubyte[] code, StaticAnalysisConfig analysisConfig, bool staticAnalyze = true)
{ {
import std.parallelism; import std.parallelism;
@ -98,19 +81,19 @@ string[] analyze(string fileName, ubyte[] code, AnalyzerCheck analyzers, bool st
BaseAnalyzer[] checks; BaseAnalyzer[] checks;
if (analyzers & AnalyzerCheck.style_check) checks ~= new StyleChecker(fileName); if (analysisConfig.style_check) checks ~= new StyleChecker(fileName);
if (analyzers & AnalyzerCheck.enum_array_literal_check) checks ~= new EnumArrayLiteralCheck(fileName); if (analysisConfig.enum_array_literal_check) checks ~= new EnumArrayLiteralCheck(fileName);
if (analyzers & AnalyzerCheck.exception_check) checks ~= new PokemonExceptionCheck(fileName); if (analysisConfig.exception_check) checks ~= new PokemonExceptionCheck(fileName);
if (analyzers & AnalyzerCheck.delete_check) checks ~= new DeleteCheck(fileName); if (analysisConfig.delete_check) checks ~= new DeleteCheck(fileName);
if (analyzers & AnalyzerCheck.float_operator_check) checks ~= new FloatOperatorCheck(fileName); if (analysisConfig.float_operator_check) checks ~= new FloatOperatorCheck(fileName);
if (analyzers & AnalyzerCheck.number_style_check) checks ~= new NumberStyleCheck(fileName); if (analysisConfig.number_style_check) checks ~= new NumberStyleCheck(fileName);
if (analyzers & AnalyzerCheck.object_const_check) checks ~= new ObjectConstCheck(fileName); if (analysisConfig.object_const_check) checks ~= new ObjectConstCheck(fileName);
if (analyzers & AnalyzerCheck.backwards_range_check) checks ~= new BackwardsRangeCheck(fileName); if (analysisConfig.backwards_range_check) checks ~= new BackwardsRangeCheck(fileName);
if (analyzers & AnalyzerCheck.if_else_same_check) checks ~= new IfElseSameCheck(fileName); if (analysisConfig.if_else_same_check) checks ~= new IfElseSameCheck(fileName);
if (analyzers & AnalyzerCheck.constructor_check) checks ~= new ConstructorCheck(fileName); if (analysisConfig.constructor_check) checks ~= new ConstructorCheck(fileName);
if (analyzers & AnalyzerCheck.unused_variable_check) checks ~= new UnusedVariableCheck(fileName); if (analysisConfig.unused_variable_check) checks ~= new UnusedVariableCheck(fileName);
if (analyzers & AnalyzerCheck.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName); if (analysisConfig.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName);
if (analyzers & AnalyzerCheck.opequals_tohash_check) checks ~= new OpEqualsWithoutToHashCheck(fileName); if (analysisConfig.opequals_tohash_check) checks ~= new OpEqualsWithoutToHashCheck(fileName);
foreach (check; checks) foreach (check; checks)
{ {

View File

@ -93,6 +93,10 @@ class StyleChecker : BaseAnalyzer
unittest unittest
{ {
import analysis.config;
StaticAnalysisConfig sac;
sac.style_check = true;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
module AMODULE; // [warn]: Module/package name 'AMODULE' does not match style guidelines. module AMODULE; // [warn]: Module/package name 'AMODULE' does not match style guidelines.
@ -105,7 +109,7 @@ unittest
interface puma {} // [warn]: Interface name 'puma' does not match style guidelines. interface puma {} // [warn]: Interface name 'puma' does not match style guidelines.
struct dog {} // [warn]: Struct name 'dog' does not match style guidelines. struct dog {} // [warn]: Struct name 'dog' does not match style guidelines.
enum racoon {} // [warn]: Enum name 'racoon' does not match style guidelines. enum racoon {} // [warn]: Enum name 'racoon' does not match style guidelines.
}c, analysis.run.AnalyzerCheck.style_check); }c, sac);
stderr.writeln("Unittest for StyleChecker passed."); stderr.writeln("Unittest for StyleChecker passed.");
} }

View File

@ -1,17 +1,19 @@
@echo off @echo off
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
set DFLAGS=-version=DIP61 -O -release -inline set DFLAGS=-O -release -inline
set CORE= set CORE=
set STD= set STD=
set STDD= set STDD=
set ANALYSIS= set ANALYSIS=
set INIFILED=
for %%x in (*.d) do set CORE=!CORE! %%x for %%x in (*.d) do set CORE=!CORE! %%x
for %%x in (std/*.d) do set STD=!STD! std/%%x for %%x in (std/*.d) do set STD=!STD! std/%%x
for %%x in (std/d/*.d) do set STDD=!STDD! std/d/%%x for %%x in (std/d/*.d) do set STDD=!STDD! std/d/%%x
for %%x in (analysis/*.d) do set ANALYSIS=!ANALYSIS! analysis/%%x for %%x in (analysis/*.d) do set ANALYSIS=!ANALYSIS! analysis/%%x
for %%x in (inifiled/source/*.d) do set INIFILED=!INIFILED! inifiled/source/%%x
@echo on @echo on
dmd %CORE% %STD% %STDD% %ANALYSIS% %DFLAGS% -ofdscanner.exe dmd %CORE% %STD% %STDD% %ANALYSIS% %INIFILED% %DFLAGS% -ofdscanner.exe

1
inifiled Submodule

@ -0,0 +1 @@
Subproject commit 256db3c28db9e3824b7ed5f646964c19f37dd32d

@ -1 +1 @@
Subproject commit d9387eb3b275295cd0263bdc273c4b0b63f29f98 Subproject commit 4b95000c560b945ed8d426553c4d71849875cc92

95
main.d
View File

@ -24,6 +24,9 @@ import astprinter;
import imports; import imports;
import outliner; import outliner;
import analysis.run; import analysis.run;
import analysis.config;
import inifiled;
int main(string[] args) int main(string[] args)
{ {
@ -52,6 +55,7 @@ int run(string[] args)
bool outline; bool outline;
bool tokenDump; bool tokenDump;
bool styleCheck; bool styleCheck;
bool defaultConfig;
try try
{ {
@ -60,7 +64,7 @@ int run(string[] args)
"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, "styleCheck", &styleCheck, "tokenDump", &tokenDump, "styleCheck", &styleCheck,
"muffinButton", &muffin); "defaultConfig", &defaultConfig, "muffinButton", &muffin);
} }
catch (ConvException e) catch (ConvException e)
{ {
@ -90,7 +94,7 @@ int run(string[] args)
} }
auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount, auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck, ast, imports, outline, tokenDump, styleCheck]); syntaxCheck, ast, imports, outline, tokenDump, styleCheck, defaultConfig]);
if (optionCount > 1) if (optionCount > 1)
{ {
stderr.writeln("Too many options specified"); stderr.writeln("Too many options specified");
@ -103,8 +107,14 @@ int run(string[] args)
} }
StringCache cache = StringCache(StringCache.defaultBucketCount); StringCache cache = StringCache(StringCache.defaultBucketCount);
if (defaultConfig)
if (tokenDump || highlight) {
string s = getConfigurationLocation();
StaticAnalysisConfig saConfig = defaultStaticAnalysisConfig();
writeln("Writing default config file to ", s);
writeINIFile(saConfig, s);
}
else if (tokenDump || highlight)
{ {
bool usingStdin = args.length == 1; bool usingStdin = args.length == 1;
ubyte[] bytes = usingStdin ? readStdin() : readFile(args[1]); ubyte[] bytes = usingStdin ? readStdin() : readFile(args[1]);
@ -133,7 +143,11 @@ int run(string[] args)
} }
else if (styleCheck) else if (styleCheck)
{ {
stdout.analyze(expandArgs(args, recursive), AnalyzerCheck.all); StaticAnalysisConfig config = defaultStaticAnalysisConfig();
string s = getConfigurationLocation();
if (s.exists())
readINIFile(config, s);
stdout.analyze(expandArgs(args, recursive), config);
} }
else if (syntaxCheck) else if (syntaxCheck)
{ {
@ -171,7 +185,25 @@ int run(string[] args)
writefln("total:\t%d", count); writefln("total:\t%d", count);
} }
} }
else if (imports || ast || outline) else if (imports)
{
string[] fileNames = usingStdin ? ["stdin"] : args[1 .. $];
LexerConfig config;
config.stringBehavior = StringBehavior.source;
auto visitor = new ImportPrinter;
foreach (name; fileNames)
{
config.fileName = name;
auto tokens = getTokensForParser(
usingStdin ? readStdin() : readFile(name),
config, &cache);
auto mod = parseModule(tokens, name, null, &doNothing);
visitor.visit(mod);
}
foreach (imp; visitor.imports[])
writeln(imp);
}
else if (ast || outline)
{ {
string fileName = usingStdin ? "stdin" : args[1]; string fileName = usingStdin ? "stdin" : args[1];
LexerConfig config; LexerConfig config;
@ -187,14 +219,8 @@ int run(string[] args)
// token.text !is null, token.index, token.line, token.column, token.type, token.comment); // token.text !is null, token.index, token.line, token.column, token.type, token.comment);
// } // }
auto mod = parseModule(tokens, fileName, null, &doNothing); auto mod = parseModule(tokens, fileName, null, &doNothing);
if (imports)
{ if (ast)
auto visitor = new ImportPrinter;
visitor.visit(mod);
foreach (imp; visitor.imports[])
writeln(imp);
}
else if (ast)
{ {
auto printer = new XMLPrinter; auto printer = new XMLPrinter;
printer.output = stdout; printer.output = stdout;
@ -294,7 +320,7 @@ options:
--styleCheck [sourceFiles] --styleCheck [sourceFiles]
Lexes and parses sourceFiles, printing the line and column number of any Lexes and parses sourceFiles, printing the line and column number of any
style guideline violations to stdout. static analysis check failures 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
@ -308,8 +334,45 @@ options:
--recursive | -R | -r --recursive | -R | -r
When used with --ctags, --tokenCount, or --sloc, dscanner will produce When used with --ctags, --tokenCount, or --sloc, dscanner will produce
ctags output for all .d and .di files contained within the given ctags output for all .d and .di files contained within the given
directories and its sub-directories.`, directories and its sub-directories.
--defaultConfig
Generates a default configuration file for the static analysis checks`,
programName); programName);
} }
void doNothing(string, size_t, size_t, string, bool) {} void doNothing(string, size_t, size_t, string, bool) {}
enum CONFIG_FILE_NAME = "dscanner.ini";
version(linux) version = useXDG;
version(BSD) version = useXDG;
version(FreeBSD) version = useXDG;
version(OSX) version = useXDG;
/**
* Locates the configuration file
*/
string getConfigurationLocation()
{
version (useXDG)
{
import std.process;
string configDir = environment.get("XDG_CONFIG_HOME", null);
if (configDir is null)
{
configDir = environment.get("HOME", null);
if (configDir is null)
throw new Exception("Both $XDG_CONFIG_HOME and $HOME are unset");
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

@ -12,9 +12,10 @@ SRC = main.d\
outliner.d\ outliner.d\
libdparse/src/std/*.d\ libdparse/src/std/*.d\
libdparse/src/std/d/*.d\ libdparse/src/std/d/*.d\
analysis/*.d analysis/*.d\
inifiled/source/*.d
INCLUDE_PATHS = -Ilibdparse/src INCLUDE_PATHS = -Ilibdparse/src
VERSIONS = -version=DIP61 VERSIONS =
all: dmdbuild all: dmdbuild

View File

@ -9,12 +9,11 @@ dmd\
highlighter.d\ highlighter.d\
ctags.d\ ctags.d\
astprinter.d\ astprinter.d\
formatter.d\
outliner.d\ outliner.d\
std/*.d\ libdparse/src/std/*.d\
std/d/*.d\ libdparse/src/std/d/*.d\
inifiled/source/*.d\
analysis/*.d\ analysis/*.d\
-version=DIP61\
-oftest\ -oftest\
-g -unittest -g -unittest