From 22104911d44bd618de09fadc1d39eee3066f89eb Mon Sep 17 00:00:00 2001 From: Matthew Brennan Jones Date: Fri, 23 May 2014 20:37:48 -0700 Subject: [PATCH] Added basic checker for duplicate attributes. --- README.md | 1 + analysis/duplicate_attribute.d | 127 +++++++++++++++++++++++++++++++++ analysis/run.d | 5 +- 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 analysis/duplicate_attribute.d diff --git a/README.md b/README.md index 5e2028e..c8f4828 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ given source files. * 'if' statements where the 'else' block is the same as the 'if' block. * Unused variables. * Unused parameters (check is skipped if function is marked "override") +* Duplicate attributes #### Wishlish * Assigning to foreach variables that are not "ref". diff --git a/analysis/duplicate_attribute.d b/analysis/duplicate_attribute.d new file mode 100644 index 0000000..c2e7870 --- /dev/null +++ b/analysis/duplicate_attribute.d @@ -0,0 +1,127 @@ +// Copyright (c) 2014, Matthew Brennan Jones +// 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.duplicate_attribute; + +import std.stdio; +import std.string; +import std.d.ast; +import std.d.lexer; +import analysis.base; +import analysis.helpers; + +// FIXME: Make it work with @safe, @trusted, @system +// FIXME: Make it work with pure, nothrow + +/** + * Checks for duplicate attributes such as @property + */ +class DuplicateAttributeCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + this(string fileName) + { + super(fileName); + } + + override void visit(const Declaration node) + { + checkAttributes(node); + node.accept(this); + } + + void checkAttributes(const Declaration node) + { + bool hasProperty = false; + + // Check the attributes + foreach (attribute; node.attributes) + { + // Just skip if missing child nodes + if (!attribute + || !attribute.storageClass + || !attribute.storageClass.atAttribute + || attribute.storageClass.atAttribute.identifier is Token.init) + continue; + + // Is a property + auto iden = attribute.storageClass.atAttribute.identifier; + checkProperty(iden, hasProperty); + } + + // Just return if missing function nodes + if (!node.functionDeclaration + || !node.functionDeclaration.memberFunctionAttributes) + return; + + // Check the functions + foreach (memberFunctionAttribute; node.functionDeclaration.memberFunctionAttributes) + { + // Just skip if missing child nodes + if (!memberFunctionAttribute + || !memberFunctionAttribute.atAttribute + || memberFunctionAttribute.atAttribute.identifier is Token.init) + continue; + + // Is a property + auto iden = memberFunctionAttribute.atAttribute.identifier; + checkProperty(iden, hasProperty); + } + } + + void checkProperty(const Token iden, ref bool hasProperty) + { + // Just return if not a property + if (!isProperty(iden)) + return; + + // Already has a property + if (hasProperty) + { + string message = "The attribute '%s' is duplicated.".format(iden.text); + addErrorMessage(iden.line, iden.column, message); + } + + // Mark it as a property + hasProperty = true; + } +} + +bool isProperty(const Token token) pure +{ + return token.type == tok!"identifier" && token.text == "property"; +} + +unittest +{ + assertAnalyzerWarnings(q{ + class CAllocator + { + @property bool xxx() // ok + { + return false; + } + + @property @property bool aaa() // [warn]: The attribute 'property' is duplicated. + { + return false; + } + + bool bbb() @property @property // [warn]: The attribute 'property' is duplicated. + { + return false; + } + + @property bool ccc() @property // [warn]: The attribute 'property' is duplicated. + { + return false; + } + } + }c, analysis.run.AnalyzerCheck.duplicate_attribute); + + stderr.writeln("Unittest for DuplicateAttributeCheck passed."); +} + diff --git a/analysis/run.d b/analysis/run.d index 8737cbc..88b1e55 100644 --- a/analysis/run.d +++ b/analysis/run.d @@ -22,6 +22,7 @@ import analysis.range; import analysis.ifelsesame; import analysis.constructors; import analysis.unused; +import analysis.duplicate_attribute; enum AnalyzerCheck : int { @@ -36,7 +37,8 @@ enum AnalyzerCheck : int if_else_same_check = 0x100, constructor_check = 0x200, unused_variable_check = 0x400, - all = 0x7FF + duplicate_attribute = 0x800, + all = 0xFFF } void messageFunction(string fileName, size_t line, size_t column, string message, @@ -104,6 +106,7 @@ string[] analyze(string fileName, ubyte[] code, AnalyzerCheck analyzers, bool st if (analyzers & AnalyzerCheck.if_else_same_check) checks ~= new IfElseSameCheck(fileName); if (analyzers & AnalyzerCheck.constructor_check) checks ~= new ConstructorCheck(fileName); if (analyzers & AnalyzerCheck.unused_variable_check) checks ~= new UnusedVariableCheck(fileName); + if (analyzers & AnalyzerCheck.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName); foreach (check; checks) {