Skip to content

Commit ac22b0e

Browse files
bergmeisterJamesWTruher
authored andcommitted
Allow TypeNotFound parser errors (#957)
* Trim out TypeNotFoundParseErrors. * Add verbose logging and test * fix test syntax * add test for -path parameter set * use pester test drive * fix tests to not run in library usage session * Address PR comments about additional assertion and xml comment * Remove unused dotnet-core NuGet feed that caused build failure due to downtime * Fix typo and move string into resources * fix compilation error (forgot to check-in file) * fix typo to also build for psv3 * Since the using statement was introduced only inn v5, move to block that runs only on v5+ * fix tests by moving the tests (clearly a smell that something is wrong in this test script * write warning on parse errors instead of verbose output and tweak message * instead of returning a warning, return a diagnostic record with a custom rule name and tweak error message * poke build due to sporadic AppVeyor failure on setup (not a test) -> rename variable
1 parent b99ceb2 commit ac22b0e

File tree

6 files changed

+83
-16
lines changed

6 files changed

+83
-16
lines changed

Engine/ScriptAnalyzer.cs

+44-12
Original file line numberDiff line numberDiff line change
@@ -1522,24 +1522,26 @@ public IEnumerable<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefini
15221522
return null;
15231523
}
15241524

1525-
if (errors != null && errors.Length > 0)
1525+
var relevantParseErrors = RemoveTypeNotFoundParseErrors(errors, out List<DiagnosticRecord> diagnosticRecords);
1526+
1527+
if (relevantParseErrors != null && relevantParseErrors.Count > 0)
15261528
{
1527-
foreach (ParseError error in errors)
1529+
foreach (var parseError in relevantParseErrors)
15281530
{
1529-
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParseErrorFormatForScriptDefinition, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
1530-
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
1531+
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParseErrorFormatForScriptDefinition, parseError.Message.TrimEnd('.'), parseError.Extent.StartLineNumber, parseError.Extent.StartColumnNumber);
1532+
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, parseError.ErrorId));
15311533
}
15321534
}
15331535

1534-
if (errors != null && errors.Length > 10)
1536+
if (relevantParseErrors != null && relevantParseErrors.Count > 10)
15351537
{
15361538
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessageForScriptDefinition);
15371539
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, scriptDefinition));
15381540

15391541
return new List<DiagnosticRecord>();
15401542
}
15411543

1542-
return this.AnalyzeSyntaxTree(scriptAst, scriptTokens, String.Empty);
1544+
return diagnosticRecords.Concat(this.AnalyzeSyntaxTree(scriptAst, scriptTokens, String.Empty));
15431545
}
15441546

15451547
/// <summary>
@@ -1644,6 +1646,33 @@ private static Encoding GetFileEncoding(string path)
16441646
}
16451647
}
16461648

1649+
/// <summary>
1650+
/// Inspects Parse errors and removes TypeNotFound errors that can be ignored since some types are not known yet (e.g. due to 'using' statements).
1651+
/// </summary>
1652+
/// <param name="parseErrors"></param>
1653+
/// <returns>List of relevant parse errors.</returns>
1654+
private List<ParseError> RemoveTypeNotFoundParseErrors(ParseError[] parseErrors, out List<DiagnosticRecord> diagnosticRecords)
1655+
{
1656+
var relevantParseErrors = new List<ParseError>();
1657+
diagnosticRecords = new List<DiagnosticRecord>();
1658+
1659+
foreach (var parseError in parseErrors)
1660+
{
1661+
// If types are not known due them not being imported yet, the parser throws an error that can be ignored
1662+
if (parseError.ErrorId != "TypeNotFound")
1663+
{
1664+
relevantParseErrors.Add(parseError);
1665+
}
1666+
else
1667+
{
1668+
diagnosticRecords.Add(new DiagnosticRecord(
1669+
string.Format(Strings.TypeNotFoundParseErrorFound, parseError.Extent), parseError.Extent, "TypeNotFound", DiagnosticSeverity.Information, parseError.Extent.File));
1670+
}
1671+
}
1672+
1673+
return relevantParseErrors;
1674+
}
1675+
16471676
private static Range SnapToEdges(EditableText text, Range range)
16481677
{
16491678
// todo add TextLines.Validate(range) and TextLines.Validate(position)
@@ -1806,6 +1835,7 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
18061835
ParseError[] errors = null;
18071836

18081837
this.outputWriter.WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseFileMessage, filePath));
1838+
var diagnosticRecords = new List<DiagnosticRecord>();
18091839

18101840
//Parse the file
18111841
if (File.Exists(filePath))
@@ -1829,17 +1859,19 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
18291859
scriptAst = Parser.ParseFile(filePath, out scriptTokens, out errors);
18301860
}
18311861
#endif //!PSV3
1862+
var relevantParseErrors = RemoveTypeNotFoundParseErrors(errors, out diagnosticRecords);
1863+
18321864
//Runspace.DefaultRunspace = oldDefault;
1833-
if (errors != null && errors.Length > 0)
1865+
if (relevantParseErrors != null && relevantParseErrors.Count > 0)
18341866
{
1835-
foreach (ParseError error in errors)
1867+
foreach (var parseError in relevantParseErrors)
18361868
{
1837-
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, error.Extent.File, error.Message.TrimEnd('.'), error.Extent.StartLineNumber, error.Extent.StartColumnNumber);
1838-
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, error.ErrorId));
1869+
string parseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorFormat, parseError.Extent.File, parseError.Message.TrimEnd('.'), parseError.Extent.StartLineNumber, parseError.Extent.StartColumnNumber);
1870+
this.outputWriter.WriteError(new ErrorRecord(new ParseException(parseErrorMessage), parseErrorMessage, ErrorCategory.ParserError, parseError.ErrorId));
18391871
}
18401872
}
18411873

1842-
if (errors != null && errors.Length > 10)
1874+
if (relevantParseErrors != null && relevantParseErrors.Count > 10)
18431875
{
18441876
string manyParseErrorMessage = String.Format(CultureInfo.CurrentCulture, Strings.ParserErrorMessage, System.IO.Path.GetFileName(filePath));
18451877
this.outputWriter.WriteError(new ErrorRecord(new ParseException(manyParseErrorMessage), manyParseErrorMessage, ErrorCategory.ParserError, filePath));
@@ -1856,7 +1888,7 @@ private IEnumerable<DiagnosticRecord> AnalyzeFile(string filePath)
18561888
return null;
18571889
}
18581890

1859-
return this.AnalyzeSyntaxTree(scriptAst, scriptTokens, filePath);
1891+
return diagnosticRecords.Concat(this.AnalyzeSyntaxTree(scriptAst, scriptTokens, filePath));
18601892
}
18611893

18621894
private bool IsModuleNotFoundError(ParseError error)

Engine/Strings.Designer.cs

+10-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Engine/Strings.resx

+3
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,7 @@
324324
<data name="SettingsObjectCouldNotBResolved" xml:space="preserve">
325325
<value>Settings object could not be resolved.</value>
326326
</data>
327+
<data name="TypeNotFoundParseErrorFound" xml:space="preserve">
328+
<value>Ignoring 'TypeNotFound' parse error on type '{0}'. Check if the specified type is correct. This can also be due the type not being known at parse time due to types imported by 'using' statements.</value>
329+
</data>
327330
</root>

NuGet.Config

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<packageSources>
44
<clear />
55
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
6-
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
76
<add key="powershell-core" value="https://powershell.myget.org/F/powershell-core/api/v3/index.json" />
87
</packageSources>
98
</configuration>

Tests/Engine/InvokeScriptAnalyzer.tests.ps1

+25
Original file line numberDiff line numberDiff line change
@@ -551,4 +551,29 @@ Describe "Test -EnableExit Switch" {
551551
"$result" | Should -Not -BeLike $reportSummaryFor1Warning
552552
}
553553
}
554+
555+
# using statements are only supported in v5+
556+
if (!$testingLibraryUsage -and ($PSVersionTable.PSVersion -ge [Version]'5.0.0')) {
557+
Describe "Handles parse errors due to unknown types" {
558+
$script = @'
559+
using namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels
560+
using namespace Microsoft.Azure.Commands.Common.Authentication.Abstractions
561+
Import-Module "AzureRm"
562+
class MyClass { [IStorageContext]$StorageContext } # This will result in a parser error due to [IStorageContext] type that comes from the using statement but is not known at parse time
563+
'@
564+
It "does not throw and detect one expected warning after the parse error has occured when using -ScriptDefintion parameter set" {
565+
$warnings = Invoke-ScriptAnalyzer -ScriptDefinition $script
566+
$warnings.Count | Should -Be 1
567+
$warnings.RuleName | Should -Be 'TypeNotFound'
568+
}
569+
570+
$testFilePath = "TestDrive:\testfile.ps1"
571+
Set-Content $testFilePath -value $script
572+
It "does not throw and detect one expected warning after the parse error has occured when using -Path parameter set" {
573+
$warnings = Invoke-ScriptAnalyzer -Path $testFilePath
574+
$warnings.Count | Should -Be 1
575+
$warnings.RuleName | Should -Be 'TypeNotFound'
576+
}
577+
}
578+
}
554579
}

tools/appveyor.psm1

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function Invoke-AppVeyorBuild {
4444
$BuildType,
4545

4646
[Parameter(Mandatory)]
47-
[ValidateSet('Release', 'PSv4Release', 'PSv4Release')]
47+
[ValidateSet('Release', 'PSv3Release', 'PSv4Release')]
4848
$BuildConfiguration,
4949

5050
[Parameter(Mandatory)]

0 commit comments

Comments
 (0)