Fix Autofix for FinalAttributeChecker (#170)

This commit is contained in:
Vladiwostok 2024-11-10 19:37:02 +02:00 committed by Albert24GG
parent 8c61dc3cad
commit ba22ec2c2b
1 changed files with 140 additions and 97 deletions

View File

@ -6,11 +6,13 @@
module dscanner.analysis.final_attribute; module dscanner.analysis.final_attribute;
import dscanner.analysis.base; import dscanner.analysis.base;
import dscanner.analysis.helpers;
import std.string : format;
import std.stdio;
import dmd.dsymbol;
import dmd.astcodegen; import dmd.astcodegen;
import dmd.dsymbol;
import dmd.tokens : Token, TOK;
import std.algorithm;
import std.array;
import std.range;
import std.string : format;
/** /**
* Checks for useless usage of the final attribute. * Checks for useless usage of the final attribute.
@ -19,10 +21,8 @@ import dmd.astcodegen;
*/ */
extern (C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd extern (C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
{ {
mixin AnalyzerInfo!"final_attribute_check";
// alias visit = BaseAnalyzerDmd!AST.visit;
alias visit = BaseAnalyzerDmd.visit; alias visit = BaseAnalyzerDmd.visit;
mixin AnalyzerInfo!"final_attribute_check";
enum Parent enum Parent
{ {
@ -41,6 +41,8 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
bool _blockFinal; bool _blockFinal;
Parent _parent = Parent.module_; Parent _parent = Parent.module_;
Token[] tokens;
enum pushPopPrivate = q{ enum pushPopPrivate = q{
const bool wasPrivate = _private; const bool wasPrivate = _private;
_private = false; _private = false;
@ -50,6 +52,24 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
extern(D) this(string fileName) extern(D) this(string fileName)
{ {
super(fileName); super(fileName);
lexFile();
}
private void lexFile()
{
import dscanner.utils : readFile;
import dmd.errorsink : ErrorSinkNull;
import dmd.globals : global;
import dmd.lexer : Lexer;
auto bytes = readFile(fileName) ~ '\0';
__gshared ErrorSinkNull errorSinkNull;
if (!errorSinkNull)
errorSinkNull = new ErrorSinkNull;
scope lexer = new Lexer(null, cast(char*) bytes, 0, bytes.length, 0, 0, errorSinkNull, &global.compileEnv);
while (lexer.nextToken() != TOK.endOfFile)
tokens ~= lexer.token;
} }
override void visit(AST.StorageClassDeclaration scd) override void visit(AST.StorageClassDeclaration scd)
@ -74,16 +94,22 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
auto sd = member.isStructDeclaration(); auto sd = member.isStructDeclaration();
auto ud = member.isUnionDeclaration(); auto ud = member.isUnionDeclaration();
if (!ud && sd && scd.stc & STC.final_) if ((scd.stc & STC.final_) != 0)
{ {
addErrorMessage(cast(ulong) sd.loc.linnum, cast(ulong) sd.loc.charnum, KEY, auto finalTokenOffset = tokens.filter!(t => t.loc.linnum == member.loc.linnum)
MSGB.format(FinalAttributeChecker.MESSAGE.struct_i)); .find!(t => t.value == TOK.final_)
} .front.loc.fileOffset;
if (ud && scd.stc & STC.final_) ulong lineNum = cast(ulong) member.loc.linnum;
{ ulong charNum = cast(ulong) member.loc.charnum;
addErrorMessage(cast(ulong) ud.loc.linnum, cast(ulong) ud.loc.charnum, KEY,
MSGB.format(FinalAttributeChecker.MESSAGE.union_i)); AutoFix fix = AutoFix.replacement(finalTokenOffset, finalTokenOffset + 6, "", "Remove final attribute");
if (!ud && sd)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.struct_i), [fix]);
if (ud)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.union_i), [fix]);
} }
member.accept(this); member.accept(this);
@ -101,15 +127,22 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
{ {
auto fd = member.isFuncDeclaration(); auto fd = member.isFuncDeclaration();
if (fd) if (fd && (fd.storage_class & STC.final_))
{ {
if (_parent == Parent.class_ && fd.storage_class & STC.final_) auto finalTokenOffset = tokens.filter!(t => t.loc.linnum == fd.loc.linnum)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, .find!(t => t.value == TOK.final_)
MSGB.format(FinalAttributeChecker.MESSAGE.class_t)); .front.loc.fileOffset;
if (_parent == Parent.interface_ && fd.storage_class & STC.final_) ulong lineNum = cast(ulong) fd.loc.linnum;
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, ulong charNum = cast(ulong) fd.loc.charnum;
MSGB.format(FinalAttributeChecker.MESSAGE.interface_t));
AutoFix fix = AutoFix.replacement(finalTokenOffset, finalTokenOffset + 6, "", "Remove final attribute");
if (_parent == Parent.class_)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.class_t), [fix]);
if (_parent == Parent.interface_)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.interface_t), [fix]);
} }
} }
@ -135,33 +168,38 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
{ {
import dmd.astenums : STC; import dmd.astenums : STC;
if (_parent == Parent.class_ && _private && fd.storage_class & STC.final_) if ((fd.storage_class & STC.final_) != 0)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, {
MSGB.format(FinalAttributeChecker.MESSAGE.class_p)); auto finalTokenOffset = tokens.filter!(t => t.loc.linnum == fd.loc.linnum)
.array()
.retro()
.find!(t => t.value == TOK.final_)
.front.loc.fileOffset;
else if (fd.storage_class & STC.final_ && (fd.storage_class & STC.static_ || _blockStatic)) ulong lineNum = cast(ulong) fd.loc.linnum;
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, ulong charNum = cast(ulong) fd.loc.charnum;
MSGB.format(FinalAttributeChecker.MESSAGE.class_s));
else if (_parent == Parent.class_ && _inFinalClass && fd.storage_class & STC.final_) AutoFix fix = AutoFix.replacement(finalTokenOffset, finalTokenOffset + 6, "", "Remove final attribute");
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY,
MSGB.format(FinalAttributeChecker.MESSAGE.class_f));
if (_parent == Parent.struct_ && fd.storage_class & STC.final_) if (_parent == Parent.class_ && _private)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.class_p), [fix]);
MSGB.format(FinalAttributeChecker.MESSAGE.struct_f)); else if (fd.storage_class & STC.static_ || _blockStatic)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.class_s), [fix]);
else if (_parent == Parent.class_ && _inFinalClass)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.class_f), [fix]);
if (_parent == Parent.union_ && fd.storage_class & STC.final_) if (_parent == Parent.struct_)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.struct_f), [fix]);
MSGB.format(FinalAttributeChecker.MESSAGE.union_f));
if (_parent == Parent.module_ && fd.storage_class & STC.final_) if (_parent == Parent.union_)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.union_f), [fix]);
MSGB.format(FinalAttributeChecker.MESSAGE.func_g));
if (_parent == Parent.function_ && fd.storage_class & STC.final_) if (_parent == Parent.module_)
addErrorMessage(cast(ulong) fd.loc.linnum, cast(ulong) fd.loc.charnum, KEY, addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.func_g), [fix]);
MSGB.format(FinalAttributeChecker.MESSAGE.func_n));
if (_parent == Parent.function_)
addErrorMessage(lineNum, charNum, KEY, MSGB.format(FinalAttributeChecker.MESSAGE.func_n), [fix]);
}
_blockStatic = false; _blockStatic = false;
mixin (pushPopPrivate); mixin (pushPopPrivate);
@ -232,7 +270,9 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
@system unittest @system unittest
{ {
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; import dscanner.analysis.config : Check, disabledConfig, StaticAnalysisConfig;
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD, assertAutoFix;
import std.stdio : stderr;
StaticAnalysisConfig sac = disabledConfig(); StaticAnalysisConfig sac = disabledConfig();
sac.final_attribute_check = Check.enabled; sac.final_attribute_check = Check.enabled;
@ -393,58 +433,61 @@ extern(C++) class FinalAttributeChecker(AST) : BaseAnalyzerDmd
} }
}, sac); }, sac);
// TODO: Check if it works and fix otherwise assertAutoFix(q{
//assertAutoFix(q{ final void foo(){} // fix
//int foo() @property { return 0; } void foo(){final void foo(){}} // fix
void foo()
//class ClassName { {
//const int confusingConst() { return 0; } // fix:0 static if (true)
//const int confusingConst() { return 0; } // fix:1 final class A{ private: final protected void foo(){}} // fix
}
//int bar() @property { return 0; } // fix:0 final struct Foo{} // fix
//int bar() @property { return 0; } // fix:1 final union Foo{} // fix
//int bar() @property { return 0; } // fix:2 class Foo{private final void foo(){}} // fix
//} class Foo{private: final void foo(){}} // fix
interface Foo{final void foo(T)(){}} // fix
//struct StructName { final class Foo{final void foo(){}} // fix
//int bar() @property { return 0; } // fix:0 private: final class Foo {public: private final void foo(){}} // fix
//} class Foo {final static void foo(){}} // fix
class Foo
//union UnionName { {
//int bar() @property { return 0; } // fix:0 void foo(){}
//} static: final void foo(){} // fix
}
//interface InterfaceName { class Foo
//int bar() @property; // fix:0 {
void foo(){}
//abstract int method(); // fix static{final void foo(){}} // fix
//} void foo(){}
//}c, q{ }
//int foo() @property { return 0; } }, q{
void foo(){} // fix
//class ClassName { void foo(){void foo(){}} // fix
//int confusingConst() const { return 0; } // fix:0 void foo()
//const(int) confusingConst() { return 0; } // fix:1 {
static if (true)
//int bar() const @property { return 0; } // fix:0 final class A{ private: protected void foo(){}} // fix
//int bar() inout @property { return 0; } // fix:1 }
//int bar() immutable @property { return 0; } // fix:2 struct Foo{} // fix
//} union Foo{} // fix
class Foo{private void foo(){}} // fix
//struct StructName { class Foo{private: void foo(){}} // fix
//int bar() const @property { return 0; } // fix:0 interface Foo{void foo(T)(){}} // fix
//} final class Foo{void foo(){}} // fix
private: final class Foo {public: private void foo(){}} // fix
//union UnionName { class Foo {static void foo(){}} // fix
//int bar() const @property { return 0; } // fix:0 class Foo
//} {
void foo(){}
//interface InterfaceName { static: void foo(){} // fix
//int bar() const @property; // fix:0 }
class Foo
//int method(); // fix {
//} void foo(){}
//}c, sac); static{void foo(){}} // fix
void foo(){}
}
}, sac, true);
stderr.writeln("Unittest for FinalAttributeChecker passed."); stderr.writeln("Unittest for FinalAttributeChecker passed.");
} }