22
22
using System . Collections ;
23
23
using System . Diagnostics ;
24
24
using System . Text ;
25
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Extensions ;
25
26
26
27
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer
27
28
{
@@ -1263,13 +1264,6 @@ internal IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token,
1263
1264
1264
1265
foreach ( var psobject in psobjects )
1265
1266
{
1266
- DiagnosticSeverity severity ;
1267
- IScriptExtent extent ;
1268
- string message = string . Empty ;
1269
- string ruleName = string . Empty ;
1270
- string ruleSuppressionID = string . Empty ;
1271
- IEnumerable < CorrectionExtent > suggestedCorrections ;
1272
-
1273
1267
if ( psobject != null && psobject . ImmediateBaseObject != null )
1274
1268
{
1275
1269
// Because error stream is merged to output stream,
@@ -1282,28 +1276,9 @@ internal IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token,
1282
1276
}
1283
1277
1284
1278
// DiagnosticRecord may not be correctly returned from external rule.
1285
- try
1279
+ if ( TryConvertPSObjectToDiagnostic ( psobject , filePath , out DiagnosticRecord diagnostic ) )
1286
1280
{
1287
- severity = ( DiagnosticSeverity ) Enum . Parse ( typeof ( DiagnosticSeverity ) , psobject . Properties [ "Severity" ] . Value . ToString ( ) ) ;
1288
- message = psobject . Properties [ "Message" ] . Value . ToString ( ) ;
1289
- extent = ( IScriptExtent ) psobject . Properties [ "Extent" ] . Value ;
1290
- ruleName = psobject . Properties [ "RuleName" ] . Value . ToString ( ) ;
1291
- ruleSuppressionID = psobject . Properties [ "RuleSuppressionID" ] . Value ? . ToString ( ) ;
1292
- suggestedCorrections = ( IEnumerable < CorrectionExtent > ) psobject . Properties [ "SuggestedCorrections" ] . Value ;
1293
- }
1294
- catch ( Exception ex )
1295
- {
1296
- this . outputWriter . WriteError ( new ErrorRecord ( ex , ex . HResult . ToString ( "X" ) , ErrorCategory . NotSpecified , this ) ) ;
1297
- continue ;
1298
- }
1299
-
1300
- if ( ! string . IsNullOrEmpty ( message ) )
1301
- {
1302
- diagnostics . Add ( new DiagnosticRecord ( message , extent , ruleName , severity , filePath )
1303
- {
1304
- SuggestedCorrections = suggestedCorrections ,
1305
- RuleSuppressionID = ruleSuppressionID ,
1306
- } ) ;
1281
+ diagnostics . Add ( diagnostic ) ;
1307
1282
}
1308
1283
}
1309
1284
}
@@ -1660,6 +1635,57 @@ public EditableText Fix(EditableText text, Range range, bool skipParsing, out Ra
1660
1635
return text ;
1661
1636
}
1662
1637
1638
+ private bool TryConvertPSObjectToDiagnostic ( PSObject psObject , string filePath , out DiagnosticRecord diagnostic )
1639
+ {
1640
+ string message = psObject . Properties [ "Message" ] ? . Value ? . ToString ( ) ;
1641
+ object extentValue = psObject . Properties [ "Extent" ] ? . Value ;
1642
+ string ruleName = psObject . Properties [ "RuleName" ] ? . Value ? . ToString ( ) ;
1643
+ string ruleSuppressionID = psObject . Properties [ "RuleSuppressionID" ] ? . Value ? . ToString ( ) ;
1644
+ CorrectionExtent [ ] suggestedCorrections = psObject . TryGetPropertyValue ( "SuggestedCorrections" , out object correctionsValue )
1645
+ ? LanguagePrimitives . ConvertTo < CorrectionExtent [ ] > ( correctionsValue )
1646
+ : null ;
1647
+ DiagnosticSeverity severity = psObject . TryGetPropertyValue ( "Severity" , out object severityValue )
1648
+ ? LanguagePrimitives . ConvertTo < DiagnosticSeverity > ( severityValue )
1649
+ : DiagnosticSeverity . Warning ;
1650
+
1651
+ bool isValid = true ;
1652
+ isValid &= CheckHasRequiredProperty ( "Message" , message ) ;
1653
+ isValid &= CheckHasRequiredProperty ( "RuleName" , ruleName ) ;
1654
+
1655
+ if ( extentValue is not null && extentValue is not IScriptExtent )
1656
+ {
1657
+ this . outputWriter . WriteError (
1658
+ new ErrorRecord (
1659
+ new ArgumentException ( $ "Property 'Extent' is expected to be of type '{ typeof ( IScriptExtent ) } ' but was instead of type '{ extentValue . GetType ( ) } '") ,
1660
+ "CustomRuleDiagnosticPropertyInvalidType" ,
1661
+ ErrorCategory . InvalidArgument ,
1662
+ this ) ) ;
1663
+ isValid = false ;
1664
+ }
1665
+
1666
+ if ( ! isValid )
1667
+ {
1668
+ diagnostic = null ;
1669
+ return false ;
1670
+ }
1671
+
1672
+ diagnostic = new DiagnosticRecord ( message , ( IScriptExtent ) extentValue , ruleName , severity , filePath , ruleSuppressionID , suggestedCorrections ) ;
1673
+ return true ;
1674
+ }
1675
+
1676
+ private bool CheckHasRequiredProperty ( string propertyName , object propertyValue )
1677
+ {
1678
+ if ( propertyValue is null )
1679
+ {
1680
+ var exception = new ArgumentNullException ( propertyName , $ "The '{ propertyName } ' property is required on custom rule diagnostics") ;
1681
+ this . outputWriter . WriteError ( new ErrorRecord ( exception , "CustomRuleDiagnosticPropertyMissing" , ErrorCategory . InvalidArgument , this ) ) ;
1682
+ return false ;
1683
+ }
1684
+
1685
+ return true ;
1686
+ }
1687
+
1688
+
1663
1689
private static Encoding GetFileEncoding ( string path )
1664
1690
{
1665
1691
using ( var stream = new FileStream ( path , FileMode . Open ) )
0 commit comments