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 class RedundantStorageClassCheck : BaseAnalyzer
20 {
21 	alias visit = BaseAnalyzer.visit;
22 	enum string REDUNDANT_VARIABLE_ATTRIBUTES = "Variable declaration for `%s` has redundant attributes (%-(`%s`%|, %)).";
23 
24 	this(string fileName, bool skipTests = false)
25 	{
26 		super(fileName, null, skipTests);
27 	}
28 
29 	override void visit(const Declaration node)
30 	{
31 		checkAttributes(node);
32 		node.accept(this);
33 	}
34 
35 	void checkAttributes(const Declaration node)
36 	{
37 		if (node.variableDeclaration !is null && node.attributes !is null)
38 			checkVariableDeclaration(node.variableDeclaration, node.attributes);
39 	}
40 
41 	void checkVariableDeclaration(const VariableDeclaration vd, const Attribute[] attributes)
42 	{
43 		import std.algorithm.comparison : among;
44 		import std.algorithm.searching: all;
45 
46 		string[] globalAttributes;
47 		foreach (attrib; attributes)
48 		{
49 			if (attrib.attribute.type.among(tok!"shared", tok!"static", tok!"__gshared", tok!"immutable"))
50 				globalAttributes ~= attrib.attribute.type.str;
51 		}
52 		if (globalAttributes.length > 1)
53 		{
54 			if (globalAttributes.length == 2 && (
55 					globalAttributes.all!(a => a.among("shared", "static")) ||
56 					globalAttributes.all!(a => a.among("static", "immutable"))
57 			))
58 				return;
59 			auto t = vd.declarators[0].name;
60 			string message = REDUNDANT_VARIABLE_ATTRIBUTES.format(t.text, globalAttributes);
61 			addErrorMessage(t.line, t.column, "dscanner.unnecessary.duplicate_attribute", message);
62 		}
63 	}
64 }
65 
66 unittest
67 {
68 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
69 
70 	StaticAnalysisConfig sac = disabledConfig();
71 	sac.redundant_storage_classes = Check.enabled;
72 
73 	// https://github.com/dlang-community/D-Scanner/issues/438
74 	assertAnalyzerWarnings(q{
75 		immutable int a;
76 
77 		immutable shared int a; // [warn]: %s
78 		shared immutable int a; // [warn]: %s
79 
80 		immutable __gshared int a; // [warn]: %s
81 		__gshared immutable int a; // [warn]: %s
82 
83 		__gshared static int a; // [warn]: %s
84 
85 		shared static int a;
86 		static shared int a;
87 		static immutable int a;
88 		immutable static int a;
89 
90 		enum int a;
91 		extern(C++) immutable int a;
92 		immutable int function(immutable int, shared int) a;
93 	}c.format(
94 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["immutable", "shared"]),
95 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["shared", "immutable"]),
96 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["immutable", "__gshared"]),
97 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["__gshared", "immutable"]),
98 		RedundantStorageClassCheck.REDUNDANT_VARIABLE_ATTRIBUTES.format("a", ["__gshared", "static"]),
99 	), sac);
100 
101 	stderr.writeln("Unittest for RedundantStorageClassCheck passed.");
102 }