Skip to content

Commit

Permalink
Cleanup code and fix integration tests (#172)
Browse files Browse the repository at this point in the history
* Delete libdparse unit test assertion function

* Delete unused libdparse visitors from base.d

* Improve StaticIfElse warning location

* Improve FunctionAttributeCheck warning location

* Switch to DMD flow for listing autofixes

* Extract dmd analyzer selection in a separate function

* Make getName() method in BaseAnalyzerDmd public

* Fix offsets in integration test json

* Improve StyleChecker warning location

* Enable integration tests in CI

* Fix Autofix flow

* Remove & comment dead code

* Remove dead code from autofix unit test

* Remove dead code

* Remove dead code from autofix.d

* Clean up code in helpers.d

* Clean up code in run.d and migrate StatsCollector to dmd

* Fix reading code from stdin

* Return if errors are found in analysis flows

* Remove dead code

* Check for Windows line terminators in integration tests
  • Loading branch information
Vladiwostok authored Dec 10, 2024
1 parent e9245f1 commit b4a9a14
Show file tree
Hide file tree
Showing 24 changed files with 553 additions and 1,150 deletions.
15 changes: 10 additions & 5 deletions .github/workflows/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,16 @@ jobs:
fi
"./bin/dscanner$EXE" --styleCheck -f "$FORMAT" src
# TODO: fixme
#- name: Integration Tests
#run: ./it.sh
#working-directory: tests
#shell: bash
- name: Integration Tests
# run: ./it.sh
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
./it.sh Windows
else
./it.sh Unix
fi
working-directory: tests
shell: bash

- name: Run style checks
if: ${{ matrix.compiler.dmd == 'dmd' && matrix.build.type == 'make' }}
Expand Down
10 changes: 5 additions & 5 deletions src/dscanner/analysis/always_curly.d
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ extern (C++) class AlwaysCurlyCheck(AST) : BaseAnalyzerDmd
unittest
{
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD, assertAutoFix;
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD;
import std.stdio : stderr;

StaticAnalysisConfig sac = disabledConfig();
Expand Down Expand Up @@ -240,7 +240,7 @@ unittest
void test() {
if(true) { return; } // fix:0
}
}c, sac, true);
}c, sac);

assertAutoFix(q{
void test() {
Expand All @@ -250,7 +250,7 @@ unittest
void test() {
foreach(_; 0 .. 10 ) { return; } // fix:0
}
}c, sac, true);
}c, sac);

assertAutoFix(q{
void test() {
Expand All @@ -260,7 +260,7 @@ unittest
void test() {
for(int i = 0; i < 10; ++i) { return; } // fix:0
}
}c, sac, true);
}c, sac);

assertAutoFix(q{
void test() {
Expand All @@ -270,7 +270,7 @@ unittest
void test() {
do { return; } while(true); // fix:0
}
}c, sac, true);
}c, sac);


stderr.writeln("Unittest for AutoFix AlwaysCurly passed.");
Expand Down
2 changes: 1 addition & 1 deletion src/dscanner/analysis/auto_function.d
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ unittest
@safe void doStuff(){} // fix
@Custom
void doStuff(){} // fix
}c, sac, true);
}c, sac);

stderr.writeln("Unittest for AutoFunctionChecker passed.");
}
155 changes: 33 additions & 122 deletions src/dscanner/analysis/autofix.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,124 +2,23 @@ module dscanner.analysis.autofix;

import std.algorithm : filter, findSplit;
import std.conv : to;
import std.file : exists, remove;
import std.functional : toDelegate;
import std.stdio;

import dparse.lexer;
import dparse.rollback_allocator;
import dparse.ast : Module;

import dsymbol.modulecache : ModuleCache;

import dscanner.analysis.base : AutoFix, AutoFixFormatting, BaseAnalyzer, Message;
import dscanner.analysis.base : AutoFix, AutoFixFormatting, BaseAnalyzer, BaseAnalyzerDmd, Message;
import dscanner.analysis.config : StaticAnalysisConfig;
import dscanner.analysis.run : analyze, doNothing;
import dscanner.utils : readFile, readStdin;

private void resolveAutoFixes(
ref Message message,
string fileName,
ref ModuleCache moduleCache,
scope const(Token)[] tokens,
const Module m,
const StaticAnalysisConfig analysisConfig,
const AutoFixFormatting overrideFormattingConfig = AutoFixFormatting.invalid
)
{
resolveAutoFixes(message.checkName, message.autofixes, fileName, moduleCache,
tokens, m, analysisConfig, overrideFormattingConfig);
}

private void resolveAutoFixes(string messageCheckName, AutoFix[] autofixes, string fileName,
ref ModuleCache moduleCache,
scope const(Token)[] tokens, const Module m,
const StaticAnalysisConfig analysisConfig,
const AutoFixFormatting overrideFormattingConfig = AutoFixFormatting.invalid)
{
import core.memory : GC;
import dsymbol.conversion.first : FirstPass;
import dsymbol.conversion.second : secondPass;
import dsymbol.scope_ : Scope;
import dsymbol.semantic : SemanticSymbol;
import dsymbol.string_interning : internString;
import dsymbol.symbol : DSymbol;
import dscanner.analysis.run : getAnalyzersForModuleAndConfig;

const(AutoFixFormatting) formattingConfig =
overrideFormattingConfig is AutoFixFormatting.invalid
? analysisConfig.getAutoFixFormattingConfig()
: overrideFormattingConfig;

scope first = new FirstPass(m, internString(fileName), &moduleCache, null);
first.run();

secondPass(first.rootSymbol, first.moduleScope, moduleCache);
auto moduleScope = first.moduleScope;
scope(exit) typeid(DSymbol).destroy(first.rootSymbol.acSymbol);
scope(exit) typeid(SemanticSymbol).destroy(first.rootSymbol);
scope(exit) typeid(Scope).destroy(first.moduleScope);

GC.disable;
scope (exit)
GC.enable;

foreach (BaseAnalyzer check; getAnalyzersForModuleAndConfig(fileName, tokens, m, analysisConfig, moduleScope))
{
if (check.getName() == messageCheckName)
{
foreach (ref autofix; autofixes)
autofix.resolveAutoFixFromCheck(check, m, tokens, formattingConfig);
return;
}
}

throw new Exception("Cannot find analyzer " ~ messageCheckName
~ " to resolve autofix with.");
}

void resolveAutoFixFromCheck(
ref AutoFix autofix,
BaseAnalyzer check,
const Module m,
scope const(Token)[] tokens,
const AutoFixFormatting formattingConfig
)
{
import std.sumtype : match;

autofix.replacements.match!(
(AutoFix.ResolveContext context) {
autofix.replacements = check.resolveAutoFix(m, tokens, context, formattingConfig);
},
(_) {}
);
}

private AutoFix.CodeReplacement[] resolveAutoFix(string messageCheckName, AutoFix.ResolveContext context,
string fileName,
ref ModuleCache moduleCache,
scope const(Token)[] tokens, const Module m,
const StaticAnalysisConfig analysisConfig,
const AutoFixFormatting overrideFormattingConfig = AutoFixFormatting.invalid)
{
AutoFix temp;
temp.replacements = context;
resolveAutoFixes(messageCheckName, (&temp)[0 .. 1], fileName, moduleCache,
tokens, m, analysisConfig, overrideFormattingConfig);
return temp.expectReplacements("resolving didn't work?!");
}
import dscanner.analysis.rundmd;
import dscanner.utils : getModuleName, readFile, readStdin;

void listAutofixes(
StaticAnalysisConfig config,
string resolveMessage,
bool usingStdin,
string fileName,
StringCache* cache,
ref ModuleCache moduleCache
string fileName
)
{
import dparse.parser : parseModule;
import dscanner.analysis.base : Message;
import std.format : format;
import std.json : JSONValue;

Expand All @@ -145,30 +44,35 @@ void listAutofixes(

bool matchesCursor(Message m)
{
return isBytes
? req.bytes >= m.startIndex && req.bytes <= m.endIndex
: req.line >= m.startLine && req.line <= m.endLine
&& (req.line > m.startLine || req.column >= m.startColumn)
&& (req.line < m.endLine || req.column <= m.endColumn);
return isBytes ? req.bytes >= m.startIndex && req.bytes <= m.endIndex
: req.line >= m.startLine && req.line <= m.endLine
&& (req.line > m.startLine || req.column >= m.startColumn)
&& (req.line < m.endLine || req.column <= m.endColumn);
}

RollbackAllocator rba;
LexerConfig lexerConfig;
lexerConfig.fileName = fileName;
lexerConfig.stringBehavior = StringBehavior.source;
auto tokens = getTokensForParser(usingStdin ? readStdin()
: readFile(fileName), lexerConfig, cache);
auto mod = parseModule(tokens, fileName, &rba, toDelegate(&doNothing));
ubyte[] code;
if (usingStdin)
{
code = readStdin();
fileName = "stdin.d";
File f = File(fileName, "w");
f.rawWrite(code);
f.close();
}
else
{
code = readFile(fileName);
}

auto messages = analyze(fileName, mod, config, moduleCache, tokens);
auto dmdModule = parseDmdModule(fileName, cast(string) code);
auto moduleName = getModuleName(dmdModule.md);
auto messages = analyzeDmd(fileName, dmdModule, moduleName, config);

with (stdout.lockingTextWriter)
{
put("[");
foreach (message; messages[].filter!matchesCursor)
{
resolveAutoFixes(message, fileName, moduleCache, tokens, mod, config);

foreach (i, autofix; message.autofixes)
{
put(i == 0 ? "\n" : ",\n");
Expand All @@ -191,6 +95,12 @@ void listAutofixes(
put("\n]");
}
stdout.flush();

if (usingStdin)
{
assert(exists(fileName));
remove(fileName);
}
}

void improveAutoFixWhitespace(scope const(char)[] code, AutoFix.CodeReplacement[] replacements)
Expand Down Expand Up @@ -227,7 +137,8 @@ void improveAutoFixWhitespace(scope const(char)[] code, AutoFix.CodeReplacement[
{
assert(replacement.range[0] >= 0 && replacement.range[0] < code.length
&& replacement.range[1] >= 0 && replacement.range[1] < code.length
&& replacement.range[0] <= replacement.range[1], "trying to autofix whitespace on code that doesn't match with what the replacements were generated for");
&& replacement.range[0] <= replacement.range[1],
"trying to autofix whitespace on code that doesn't match with what the replacements were generated for");

void growRight()
{
Expand Down
Loading

0 comments on commit b4a9a14

Please sign in to comment.