1 //          Copyright Brian Schott (Hackerpilot) 2014.
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.local_imports;
7 
8 import std.stdio;
9 import dparse.ast;
10 import dparse.lexer;
11 import dscanner.analysis.base;
12 import dscanner.analysis.helpers;
13 import dsymbol.scope_;
14 
15 /**
16  * Checks for local imports that import all symbols.
17  * See_also: $(LINK https://issues.dlang.org/show_bug.cgi?id=10378)
18  */
19 final class LocalImportCheck : BaseAnalyzer
20 {
21 	alias visit = BaseAnalyzer.visit;
22 
23 	mixin AnalyzerInfo!"local_import_check";
24 
25 	/**
26 	 * Construct with the given file name.
27 	 */
28 	this(string fileName, const(Scope)* sc, bool skipTests = false)
29 	{
30 		super(fileName, sc, skipTests);
31 	}
32 
33 	mixin visitThing!StructBody;
34 	mixin visitThing!BlockStatement;
35 
36 	override void visit(const Declaration dec)
37 	{
38 		if (dec.importDeclaration is null)
39 		{
40 			dec.accept(this);
41 			return;
42 		}
43 		foreach (attr; dec.attributes)
44 		{
45 			if (attr.attribute == tok!"static")
46 				isStatic = true;
47 		}
48 		dec.accept(this);
49 		isStatic = false;
50 	}
51 
52 	override void visit(const ImportDeclaration id)
53 	{
54 		if ((!isStatic && interesting) && (id.importBindings is null
55 				|| id.importBindings.importBinds.length == 0))
56 		{
57 			foreach (singleImport; id.singleImports)
58 			{
59 				if (singleImport.rename.text.length == 0)
60 				{
61 					addErrorMessage(singleImport.identifierChain.identifiers[0].line,
62 							singleImport.identifierChain.identifiers[0].column,
63 							"dscanner.suspicious.local_imports", "Local imports should specify"
64 							~ " the symbols being imported to avoid hiding local symbols.");
65 				}
66 			}
67 		}
68 	}
69 
70 private:
71 
72 	mixin template visitThing(T)
73 	{
74 		override void visit(const T thing)
75 		{
76 			const b = interesting;
77 			interesting = true;
78 			thing.accept(this);
79 			interesting = b;
80 		}
81 	}
82 
83 	bool interesting;
84 	bool isStatic;
85 }
86 
87 unittest
88 {
89 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
90 
91 	StaticAnalysisConfig sac = disabledConfig();
92 	sac.local_import_check = Check.enabled;
93 	assertAnalyzerWarnings(q{
94 		void testLocalImport()
95 		{
96 			import std.stdio; // [warn]: Local imports should specify the symbols being imported to avoid hiding local symbols.
97 			import std.fish : scales, head;
98 			import DAGRON = std.experimental.dragon;
99 		}
100 	}}, sac);
101 
102 	stderr.writeln("Unittest for LocalImportCheck passed.");
103 }