17
17
using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
18
18
using System . ComponentModel . Composition ;
19
19
using System . Globalization ;
20
+ using System . Text . RegularExpressions ;
20
21
21
22
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
22
23
{
23
24
/// <summary>
24
- /// UseManifestExportFields: Run Test Module Manifest to check that no deprecated fields are being used.
25
+ /// UseToExportFieldsInManifest: Checks if AliasToExport, CmdletsToExport, FunctionsToExport and VariablesToExport
26
+ /// fields do not use wildcards and $null in their entries.
25
27
/// </summary>
26
28
[ Export ( typeof ( IScriptRule ) ) ]
27
- public class UseManifestExportFields : IScriptRule
29
+ public class UseToExportFieldsInManifest : IScriptRule
28
30
{
29
31
/// <summary>
30
- /// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used.
32
+ /// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport
33
+ /// and VariablesToExport fields do not use wildcards and $null in their entries.
31
34
/// </summary>
32
35
/// <param name="ast">The script's ast</param>
33
36
/// <param name="fileName">The script's file name</param>
@@ -44,37 +47,62 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
44
47
yield break ;
45
48
}
46
49
50
+ if ( ! IsValidManifest ( ast , fileName ) )
51
+ {
52
+ yield break ;
53
+ }
54
+
47
55
String [ ] manifestFields = { "FunctionsToExport" , "CmdletsToExport" , "VariablesToExport" , "AliasesToExport" } ;
48
56
var hashtableAst = ast . Find ( x => x is HashtableAst , false ) as HashtableAst ;
49
-
57
+
50
58
if ( hashtableAst == null )
51
- {
52
- //Should we emit a warning if the parser cannot find a hashtable?
59
+ {
53
60
yield break ;
54
61
}
55
62
56
63
foreach ( String field in manifestFields )
57
64
{
58
65
IScriptExtent extent ;
59
- if ( ! HasAcceptableExportField ( field , hashtableAst , out extent ) && extent != null )
66
+ if ( ! HasAcceptableExportField ( field , hashtableAst , ast . Extent . Text , out extent ) && extent != null )
60
67
{
61
68
yield return new DiagnosticRecord ( GetError ( field ) , extent , GetName ( ) , DiagnosticSeverity . Warning , fileName ) ;
62
69
}
63
70
}
64
71
65
72
}
66
73
67
- private bool HasAcceptableExportField ( string key , HashtableAst hast , out IScriptExtent extent )
74
+ /// <summary>
75
+ /// Checks if the manifest file is valid.
76
+ /// </summary>
77
+ /// <param name="ast"></param>
78
+ /// <param name="fileName"></param>
79
+ /// <returns>A boolean value indicating the validity of the manifest file.</returns>
80
+ private bool IsValidManifest ( Ast ast , string fileName )
81
+ {
82
+ var missingManifestRule = new MissingModuleManifestField ( ) ;
83
+ return ! missingManifestRule . AnalyzeScript ( ast , fileName ) . GetEnumerator ( ) . MoveNext ( ) ;
84
+
85
+ }
86
+
87
+ /// <summary>
88
+ /// Checks if the *ToExport fields are explicitly set to lists, @(...)
89
+ /// </summary>
90
+ /// <param name="key"></param>
91
+ /// <param name="hast"></param>
92
+ /// <param name="scriptText"></param>
93
+ /// <param name="extent"></param>
94
+ /// <returns>A boolean value indicating if the the ToExport fields are explicitly set to lists or not.</returns>
95
+ private bool HasAcceptableExportField ( string key , HashtableAst hast , string scriptText , out IScriptExtent extent )
68
96
{
69
97
extent = null ;
70
98
foreach ( var pair in hast . KeyValuePairs )
71
99
{
72
100
if ( key . Equals ( pair . Item1 . Extent . Text . Trim ( ) , StringComparison . OrdinalIgnoreCase ) )
73
101
{
74
- var arrayAst = pair . Item2 . Find ( x => x is ArrayLiteralAst , true ) as ArrayLiteralAst ;
102
+ var arrayAst = pair . Item2 . Find ( x => x is ArrayLiteralAst || x is ArrayExpressionAst , true ) ;
75
103
if ( arrayAst == null )
76
104
{
77
- extent = GetScriptExtent ( pair ) ;
105
+ extent = GetScriptExtent ( pair , scriptText ) ;
78
106
return false ;
79
107
}
80
108
else
@@ -85,23 +113,31 @@ private bool HasAcceptableExportField(string key, HashtableAst hast, out IScript
85
113
}
86
114
return true ;
87
115
}
88
-
89
-
90
- private ScriptExtent GetScriptExtent ( Tuple < ExpressionAst , StatementAst > pair )
116
+
117
+ /// <summary>
118
+ /// Gets the script extent.
119
+ /// </summary>
120
+ /// <param name="pair"></param>
121
+ /// <param name="scriptText"></param>
122
+ /// <returns></returns>
123
+ private ScriptExtent GetScriptExtent ( Tuple < ExpressionAst , StatementAst > pair , string scriptText )
91
124
{
92
- return new ScriptExtent ( new ScriptPosition ( pair . Item1 . Extent . StartScriptPosition . File ,
93
- pair . Item1 . Extent . StartScriptPosition . LineNumber ,
94
- pair . Item1 . Extent . StartScriptPosition . Offset ,
95
- pair . Item1 . Extent . StartScriptPosition . Line ) ,
96
- new ScriptPosition ( pair . Item2 . Extent . EndScriptPosition . File ,
97
- pair . Item2 . Extent . EndScriptPosition . LineNumber ,
98
- pair . Item2 . Extent . EndScriptPosition . Offset ,
99
- pair . Item2 . Extent . EndScriptPosition . Line ) ) ;
125
+ string [ ] scriptLines = Regex . Split ( scriptText , "\r \n |\r |\n " ) ;
126
+ return new ScriptExtent ( new ScriptPosition ( pair . Item1 . Extent . File ,
127
+ pair . Item1 . Extent . StartLineNumber ,
128
+ pair . Item1 . Extent . StartColumnNumber ,
129
+ scriptLines [ pair . Item1 . Extent . StartLineNumber - 1 ] ) , //line number begins with 1
130
+ new ScriptPosition ( pair . Item2 . Extent . File ,
131
+ pair . Item2 . Extent . EndLineNumber ,
132
+ pair . Item2 . Extent . EndColumnNumber ,
133
+ scriptLines [ pair . Item2 . Extent . EndLineNumber - 1 ] ) ) ; //line number begins with 1
134
+
135
+
100
136
}
101
137
102
138
public string GetError ( string field )
103
139
{
104
- return string . Format ( CultureInfo . CurrentCulture , Strings . UseManifestExportFieldsError , field ) ;
140
+ return string . Format ( CultureInfo . CurrentCulture , Strings . UseToExportFieldsInManifestError , field ) ;
105
141
}
106
142
107
143
/// <summary>
@@ -110,7 +146,7 @@ public string GetError(string field)
110
146
/// <returns>The name of this rule</returns>
111
147
public string GetName ( )
112
148
{
113
- return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . UseManifestExportFieldsName ) ;
149
+ return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . UseToExportFieldsInManifestName ) ;
114
150
}
115
151
116
152
/// <summary>
@@ -119,7 +155,7 @@ public string GetName()
119
155
/// <returns>The common name of this rule</returns>
120
156
public string GetCommonName ( )
121
157
{
122
- return String . Format ( CultureInfo . CurrentCulture , Strings . UseManifestExportFieldsCommonName ) ;
158
+ return String . Format ( CultureInfo . CurrentCulture , Strings . UseToExportFieldsInManifestCommonName ) ;
123
159
}
124
160
125
161
/// <summary>
@@ -128,7 +164,7 @@ public string GetCommonName()
128
164
/// <returns>The description of this rule</returns>
129
165
public string GetDescription ( )
130
166
{
131
- return String . Format ( CultureInfo . CurrentCulture , Strings . UseManifestExportFieldsDescription ) ;
167
+ return String . Format ( CultureInfo . CurrentCulture , Strings . UseToExportFieldsInManifestDescription ) ;
132
168
}
133
169
134
170
/// <summary>
0 commit comments