1 // Copyright (c) 2018, dlang-community
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dscanner.analysis.redundant_storage_class;
7 
8 import std.stdio;
9 import std.string;
10 import dparse.ast;
11 import dparse.lexer;
12 import dscanner.analysis.base;
13 import dscanner.analysis.helpers;
14 import dsymbol.scope_ : Scope;
15 
16 /**
17  * Checks for redundant storage classes such immutable and __gshared, static and __gshared
18  */
19 final class RedundantStorageClassCheck : BaseAnalyzer
20 {
21 	alias visit = BaseAnalyzer.visit;
22 	enum string REDUNDANT_VARIABLE_ATTRIBUTES = "Variable declaration for `%s` has redundant attributes (%-(`%s`%|, %)).";
23 	mixin AnalyzerInfo!"redundant_storage_classes";
24 
25 	this(string fileName, bool skipTests = false)
26 	{
27 		super(fileName, null, skipTests);
28 	}
29 
30 	override void visit(const Declaration node)
31 	{
32 		checkAttributes(node);
33 		node.accept(this);
34 	}
35 
36 	void checkAttributes(const Declaration node)
37 	{
38 		if (node.variableDeclaration !is null && node.attributes !is null)
39 			checkVariableDeclaration(node.variableDeclaration, node.attributes);
40 	}
41 
42 	void checkVariableDeclaration(const VariableDeclaration vd, const Attribute[] attributes)
43 	{
44 		import std.algorithm.comparison : among;
45 		import std.algorithm.searching: all;
46 
47 		string[] globalAttributes;
48 		foreach (attrib; attributes)
49 		{
50 			if (attrib.attribute.type.among(tok!"shared", tok!"static", tok!"__gshared", tok!"immutable"))
51 				globalAttributes ~= attrib.attribute.type.str;
52 		}
53 		if (globalAttributes.length > 1)
54 		{
55 			if (globalAttributes.length == 2 && (
56 					globalAttributes.all!(a => a.among("shared", "static")) ||
57 					globalAttributes.all!(a => a.among("static", "immutable"))
58 			))
59 				return;
60 			auto t = vd.declarators[0].name;
61 			string message = REDUNDANT_VARIABLE_ATTRIBUTES.format(t.text, globalAttributes);
62 			addErrorMessage(t.line, t.column, "dscanner.unnecessary.duplicate_attribute", message);
63 		}
64 	}
65 }
66 
67 unittest
68 {
69 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
70 
71 	StaticAnalysisConfig sac = disabledConfig();
72 	sac.redundant_storage_classes = Check.enabled;
73 
74 	// https://github.com/dlang-community/D-Scanner/issues/438
75 	assertAnalyzerWarnings(q{
76 		immutable int a;
77 
78 		immutable shared int a; // [warn]: %s
79 		shared immutable int a; // [warn]: %s
80 
81 		immutable __gshared int a; // [warn]: %s
82 		__gshared immutable int a; // [warn]: %s
83 
84 		__gshared static int a; // [warn]: %s
85 
86 		shared static int a;
87 		static shared int a;
88 		static immutable int a;
89 		immutable static int a;
90 
91 		enum int a;
92 		extern(C++) immutable int a;
93 		immutable int function(immutable int, shared int) a;
94 	}}.format(
95 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["immutable", "shared"]),
96 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["shared", "immutable"]),
97 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["immutable", "__gshared"]),
98 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["__gshared", "immutable"]),
99 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["__gshared", "static"]),
100 	), sac);
101 
102 	stderr.writeln("Unittest for RedundantStorageClassCheck passed.");
103 }