diff --git a/src/dscanner/analysis/asm_style.d b/src/dscanner/analysis/asm_style.d index 60db029d..73a6e532 100644 --- a/src/dscanner/analysis/asm_style.d +++ b/src/dscanner/analysis/asm_style.d @@ -32,11 +32,13 @@ final class AsmStyleCheck : BaseAnalyzer if (brExp.asmBrExp !is null && brExp.asmBrExp.asmUnaExp !is null && brExp.asmBrExp.asmUnaExp.asmPrimaryExp !is null) { - addErrorMessage(brExp, "dscanner.confusing.brexp", + addErrorMessage(brExp, KEY, "This is confusing because it looks like an array index. Rewrite a[1] as [a + 1] to clarify."); } brExp.accept(this); } + + private enum string KEY = "dscanner.confusing.brexp"; } unittest diff --git a/src/dscanner/analysis/del.d b/src/dscanner/analysis/del.d index dc6a37de..a97de00d 100644 --- a/src/dscanner/analysis/del.d +++ b/src/dscanner/analysis/del.d @@ -27,12 +27,14 @@ final class DeleteCheck : BaseAnalyzer override void visit(const DeleteExpression d) { - addErrorMessage(d.tokens[0], "dscanner.deprecated.delete_keyword", + addErrorMessage(d.tokens[0], KEY, "Avoid using the 'delete' keyword.", [AutoFix.replacement(d.tokens[0], `destroy(`, "Replace delete with destroy()") .concat(AutoFix.insertionAfter(d.tokens[$ - 1], ")"))]); d.accept(this); } + + private enum string KEY = "dscanner.deprecated.delete_keyword"; } unittest diff --git a/src/dscanner/analysis/duplicate_attribute.d b/src/dscanner/analysis/duplicate_attribute.d index 7e6ec446..647e576a 100644 --- a/src/dscanner/analysis/duplicate_attribute.d +++ b/src/dscanner/analysis/duplicate_attribute.d @@ -93,7 +93,7 @@ final class DuplicateAttributeCheck : BaseAnalyzer if (hasAttribute) { string message = "Attribute '%s' is duplicated.".format(attributeName); - addErrorMessage(tokens, "dscanner.unnecessary.duplicate_attribute", message, + addErrorMessage(tokens, KEY, message, [AutoFix.replacement(tokens, "", "Remove second attribute " ~ attributeName)]); } @@ -149,6 +149,8 @@ final class DuplicateAttributeCheck : BaseAnalyzer return null; } + + private enum string KEY = "dscanner.unnecessary.duplicate_attribute"; } unittest diff --git a/src/dscanner/analysis/enumarrayliteral.d b/src/dscanner/analysis/enumarrayliteral.d index a2e69dd5..96fcc0ca 100644 --- a/src/dscanner/analysis/enumarrayliteral.d +++ b/src/dscanner/analysis/enumarrayliteral.d @@ -47,7 +47,7 @@ final class EnumArrayLiteralCheck : BaseAnalyzer if (part.initializer.nonVoidInitializer.arrayInitializer is null) continue; addErrorMessage(part.initializer.nonVoidInitializer, - "dscanner.performance.enum_array_literal", + KEY, "This enum may lead to unnecessary allocation at run-time." ~ " Use 'static immutable " ~ part.identifier.text ~ " = [ ...' instead.", @@ -58,6 +58,8 @@ final class EnumArrayLiteralCheck : BaseAnalyzer } autoDec.accept(this); } + + private enum string KEY = "dscanner.performance.enum_array_literal"; } unittest diff --git a/src/dscanner/analysis/has_public_example.d b/src/dscanner/analysis/has_public_example.d index c5040506..d8f21b78 100644 --- a/src/dscanner/analysis/has_public_example.d +++ b/src/dscanner/analysis/has_public_example.d @@ -88,6 +88,8 @@ final class HasPublicExampleCheck : BaseAnalyzer private: + enum string KEY = "dscanner.style.has_public_example"; + bool hasDitto(Decl)(const Decl decl) { import ddoc.comments : parseComment; @@ -164,7 +166,7 @@ private: { import std.string : format; - addErrorMessage(tokens, "dscanner.style.has_public_example", name is null + addErrorMessage(tokens, KEY, name is null ? "Public declaration has no documented example." : format("Public declaration '%s' has no documented example.", name)); } diff --git a/src/dscanner/analysis/ifelsesame.d b/src/dscanner/analysis/ifelsesame.d index 13dcbe8f..ca8a3ee8 100644 --- a/src/dscanner/analysis/ifelsesame.d +++ b/src/dscanner/analysis/ifelsesame.d @@ -39,7 +39,7 @@ final class IfElseSameCheck : BaseAnalyzer // extend 1 past, so we include the `else` token tokens = (tokens.ptr - 1)[0 .. tokens.length + 1]; addErrorMessage(tokens, - "dscanner.bugs.if_else_same", "'Else' branch is identical to 'Then' branch."); + IF_ELSE_SAME_KEY, "'Else' branch is identical to 'Then' branch."); } ifStatement.accept(this); } @@ -50,7 +50,7 @@ final class IfElseSameCheck : BaseAnalyzer if (e !is null && assignExpression.operator == tok!"=" && e.ternaryExpression == assignExpression.ternaryExpression) { - addErrorMessage(assignExpression, "dscanner.bugs.self_assignment", + addErrorMessage(assignExpression, SELF_ASSIGNMENT_KEY, "Left side of assignment operatior is identical to the right side."); } assignExpression.accept(this); @@ -62,7 +62,7 @@ final class IfElseSameCheck : BaseAnalyzer && andAndExpression.left == andAndExpression.right) { addErrorMessage(andAndExpression.right, - "dscanner.bugs.logic_operator_operands", + LOGIC_OPERATOR_OPERANDS_KEY, "Left side of logical and is identical to right side."); } andAndExpression.accept(this); @@ -74,11 +74,17 @@ final class IfElseSameCheck : BaseAnalyzer && orOrExpression.left == orOrExpression.right) { addErrorMessage(orOrExpression.right, - "dscanner.bugs.logic_operator_operands", + LOGIC_OPERATOR_OPERANDS_KEY, "Left side of logical or is identical to right side."); } orOrExpression.accept(this); } + +private: + + enum string IF_ELSE_SAME_KEY = "dscanner.bugs.if_else_same"; + enum string SELF_ASSIGNMENT_KEY = "dscanner.bugs.self_assignment"; + enum string LOGIC_OPERATOR_OPERANDS_KEY = "dscanner.bugs.logic_operator_operands"; } unittest diff --git a/src/dscanner/analysis/label_var_same_name_check.d b/src/dscanner/analysis/label_var_same_name_check.d index 86fd8a74..b915006b 100644 --- a/src/dscanner/analysis/label_var_same_name_check.d +++ b/src/dscanner/analysis/label_var_same_name_check.d @@ -60,6 +60,8 @@ final class LabelVarNameCheck : ScopedBaseAnalyzer private: + enum string KEY = "dscanner.suspicious.label_var_same_name"; + Thing[string][] stack; template AggregateVisit(NodeType) @@ -88,7 +90,7 @@ private: { immutable thisKind = fromLabel ? "Label" : "Variable"; immutable otherKind = thing.isVar ? "variable" : "label"; - addErrorMessage(name, "dscanner.suspicious.label_var_same_name", + addErrorMessage(name, KEY, thisKind ~ " \"" ~ fqn ~ "\" has the same name as a " ~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ "."); } diff --git a/src/dscanner/analysis/length_subtraction.d b/src/dscanner/analysis/length_subtraction.d index 197c1522..40a586ee 100644 --- a/src/dscanner/analysis/length_subtraction.d +++ b/src/dscanner/analysis/length_subtraction.d @@ -18,6 +18,8 @@ import dsymbol.scope_; */ final class LengthSubtractionCheck : BaseAnalyzer { + private enum string KEY = "dscanner.suspicious.length_subtraction"; + alias visit = BaseAnalyzer.visit; mixin AnalyzerInfo!"length_subtraction_check"; @@ -40,7 +42,7 @@ final class LengthSubtractionCheck : BaseAnalyzer if (l.identifierOrTemplateInstance is null || l.identifierOrTemplateInstance.identifier.text != "length") goto end; - addErrorMessage(addExpression, "dscanner.suspicious.length_subtraction", + addErrorMessage(addExpression, KEY, "Avoid subtracting from '.length' as it may be unsigned.", [ AutoFix.insertionBefore(l.tokens[0], "cast(ptrdiff_t) ", "Cast to ptrdiff_t") diff --git a/src/dscanner/analysis/local_imports.d b/src/dscanner/analysis/local_imports.d index 41e1e5f8..f6df2b24 100644 --- a/src/dscanner/analysis/local_imports.d +++ b/src/dscanner/analysis/local_imports.d @@ -59,7 +59,7 @@ final class LocalImportCheck : BaseAnalyzer if (singleImport.rename.text.length == 0) { addErrorMessage(singleImport, - "dscanner.suspicious.local_imports", "Local imports should specify" + KEY, "Local imports should specify" ~ " the symbols being imported to avoid hiding local symbols."); } } @@ -68,6 +68,8 @@ final class LocalImportCheck : BaseAnalyzer private: + enum string KEY = "dscanner.suspicious.local_imports"; + mixin template visitThing(T) { override void visit(const T thing) diff --git a/src/dscanner/analysis/numbers.d b/src/dscanner/analysis/numbers.d index bc95cc19..a9629966 100644 --- a/src/dscanner/analysis/numbers.d +++ b/src/dscanner/analysis/numbers.d @@ -39,12 +39,15 @@ public: && ((t.text.startsWith("0b") && !t.text.matchFirst(badBinaryRegex) .empty) || !t.text.matchFirst(badDecimalRegex).empty)) { - addErrorMessage(t, "dscanner.style.number_literals", + addErrorMessage(t, KEY, "Use underscores to improve number constant readability."); } } private: + + enum string KEY = "dscanner.style.number_literals"; + auto badBinaryRegex = ctRegex!(`^0b[01]{9,}`); auto badDecimalRegex = ctRegex!(`^\d{5,}`); } diff --git a/src/dscanner/analysis/objectconst.d b/src/dscanner/analysis/objectconst.d index b5d50a7f..e000fe52 100644 --- a/src/dscanner/analysis/objectconst.d +++ b/src/dscanner/analysis/objectconst.d @@ -68,7 +68,7 @@ final class ObjectConstCheck : BaseAnalyzer if (inAggregate && !constColon && !constBlock && !isDeclationDisabled && isInteresting(fd.name.text) && !hasConst(fd.memberFunctionAttributes)) { - addErrorMessage(d.functionDeclaration.name, "dscanner.suspicious.object_const", + addErrorMessage(d.functionDeclaration.name, KEY, "Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const."); } } @@ -81,7 +81,11 @@ final class ObjectConstCheck : BaseAnalyzer constBlock = false; } - private static bool hasConst(const MemberFunctionAttribute[] attributes) +private: + + enum string KEY = "dscanner.suspicious.object_const"; + + static bool hasConst(const MemberFunctionAttribute[] attributes) { import std.algorithm : any; @@ -89,15 +93,14 @@ final class ObjectConstCheck : BaseAnalyzer || a.tokenType == tok!"immutable" || a.tokenType == tok!"inout"); } - private static bool isInteresting(string name) + static bool isInteresting(string name) { return name == "opCmp" || name == "toHash" || name == "opEquals" || name == "toString" || name == "opCast"; } - private bool constBlock; - private bool constColon; - + bool constBlock; + bool constColon; } unittest diff --git a/src/dscanner/analysis/redundant_storage_class.d b/src/dscanner/analysis/redundant_storage_class.d index 75a0bf48..2570c566 100644 --- a/src/dscanner/analysis/redundant_storage_class.d +++ b/src/dscanner/analysis/redundant_storage_class.d @@ -59,9 +59,11 @@ final class RedundantStorageClassCheck : BaseAnalyzer return; auto t = vd.declarators[0].name; string message = REDUNDANT_VARIABLE_ATTRIBUTES.format(t.text, globalAttributes); - addErrorMessage(t, "dscanner.unnecessary.duplicate_attribute", message); + addErrorMessage(t, KEY, message); } } + + private enum string KEY = "dscanner.unnecessary.duplicate_attribute"; } unittest diff --git a/src/dscanner/analysis/undocumented.d b/src/dscanner/analysis/undocumented.d index 815ed008..6e44281b 100644 --- a/src/dscanner/analysis/undocumented.d +++ b/src/dscanner/analysis/undocumented.d @@ -146,6 +146,8 @@ final class UndocumentedDeclarationCheck : BaseAnalyzer private: + enum string KEY = "dscanner.style.undocumented_declaration"; + mixin template V(T) { override void visit(const T declaration) @@ -223,7 +225,7 @@ private: { import std.string : format; - addErrorMessage(range, "dscanner.style.undocumented_declaration", name is null + addErrorMessage(range, KEY, name is null ? "Public declaration is undocumented." : format("Public declaration '%s' is undocumented.", name)); } diff --git a/src/dscanner/analysis/unmodified.d b/src/dscanner/analysis/unmodified.d index 79663beb..b6051cd4 100644 --- a/src/dscanner/analysis/unmodified.d +++ b/src/dscanner/analysis/unmodified.d @@ -189,6 +189,8 @@ final class UnmodifiedFinder : BaseAnalyzer private: + enum string KEY = "dscanner.suspicious.unmodified"; + template PartsMightModify(T) { override void visit(const T t) @@ -300,7 +302,7 @@ private: { immutable string errorMessage = "Variable " ~ vi.name ~ " is never modified and could have been declared const or immutable."; - addErrorMessage(vi.token, "dscanner.suspicious.unmodified", errorMessage); + addErrorMessage(vi.token, KEY, errorMessage); } tree = tree[0 .. $ - 1]; } diff --git a/src/dscanner/analysis/unused_label.d b/src/dscanner/analysis/unused_label.d index b5d7efe8..fac4174a 100644 --- a/src/dscanner/analysis/unused_label.d +++ b/src/dscanner/analysis/unused_label.d @@ -115,6 +115,8 @@ final class UnusedLabelCheck : BaseAnalyzer private: + enum string KEY = "dscanner.suspicious.unused_label"; + static struct Label { string name; @@ -144,7 +146,7 @@ private: } else if (!label.used) { - addErrorMessage(label.token, "dscanner.suspicious.unused_label", + addErrorMessage(label.token, KEY, "Label \"" ~ label.name ~ "\" is not used."); } } diff --git a/src/dscanner/analysis/useless_initializer.d b/src/dscanner/analysis/useless_initializer.d index 75966129..f2867d19 100644 --- a/src/dscanner/analysis/useless_initializer.d +++ b/src/dscanner/analysis/useless_initializer.d @@ -34,7 +34,7 @@ final class UselessInitializerChecker : BaseAnalyzer private: - enum key = "dscanner.useless-initializer"; + enum string KEY = "dscanner.useless-initializer"; version(unittest) { @@ -161,7 +161,7 @@ public: { void warn(const BaseNode range) { - addErrorMessage(range, key, msg); + addErrorMessage(range, KEY, msg); } } else @@ -169,7 +169,7 @@ public: import std.format : format; void warn(const BaseNode range) { - addErrorMessage(range, key, msg.format(declarator.name.text)); + addErrorMessage(range, KEY, msg.format(declarator.name.text)); } }