| File: OptionsValidator.vb | Web Access |
| Project: src\roslyn\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic) |
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Generic Imports System.Linq Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic ''' <summary> ''' This class is used to validate the compiler options. ''' </summary> Friend Module OptionsValidator ''' <summary> ''' Parse the project level imports, adding errors to the errorBag as necessary ''' </summary> Friend Function ParseImports(importsClauses As IEnumerable(Of String), diagnostics As DiagnosticBag) As GlobalImport() Debug.Assert(diagnostics IsNot Nothing) Dim importsClauseArray = importsClauses.Select(AddressOf StringExtensions.Unquote).ToArray() If importsClauseArray.Length > 0 Then ' Create a file with Import statement for each imported name, and parse it. Use two newlines ' after each to avoid issues with implicit line continuation. Dim importFileText As String = importsClauseArray.Select(Function(name) "Imports " + name + vbCrLf + vbCrLf).Aggregate(Function(a, b) a & b) Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(importFileText), VisualBasicParseOptions.Default, "") ' Extract all the parsed imports back out. Dim parsedImportList As New List(Of GlobalImport) Dim importList As SyntaxList(Of ImportsStatementSyntax) = tree.GetCompilationUnitRoot().Imports For i = 0 To importList.Count - 1 Dim statement = importList(i) Dim importsClausesSyntaxList As SeparatedSyntaxList(Of ImportsClauseSyntax) = statement.ImportsClauses If importsClauses.Count > 0 Then Dim clause As ImportsClauseSyntax = importsClausesSyntaxList(0) Dim syntaxErrors As IEnumerable(Of Diagnostic) = clause.GetSyntaxErrors(tree) If importsClausesSyntaxList.Count > 1 Then ' Only allow one import clause per name. If more than one is found, report "expected end of statement". syntaxErrors = syntaxErrors.Concat(New VBDiagnostic(New DiagnosticInfo(MessageProvider.Instance, ERRID.ERR_ExpectedEOS), importsClausesSyntaxList(1).GetLocation())) End If Dim import = New GlobalImport(clause, importsClauseArray(i)) Dim errors = From diag In syntaxErrors Select import.MapDiagnostic(diag) diagnostics.AddRange(errors) If Not errors.Any(Function(diag) diag.Severity = DiagnosticSeverity.Error) Then ' only add imports without syntax errors. parsedImportList.Add(import) End If End If Next Return parsedImportList.ToArray() Else Return Array.Empty(Of GlobalImport) End If End Function ''' <summary> ''' Validate and parse the root namespace. If the root namespace string is a valid namespace name, the parsed ''' version is returned. Otherwise Nothing is returned. ''' </summary> Friend Function IsValidNamespaceName(name As String) As Boolean Debug.Assert(name IsNot Nothing) ' TODO: consider calling Syntax.ParseName and analyze the result (reject () or ' type characters instead of reimplementing a lot of logic in IsValidRootNamespaceComponent. ' PERF: Avoid allocations from String.Split Dim start As Integer = 0 Do Dim index = name.IndexOf("."c, start) If Not IsValidRootNamespaceComponent(name, start, If(index < 0, name.Length, index), allowEscaping:=True) Then ' ERRID.ERR_BadNamespaceName1 Return False End If If index < 0 Then Exit Do start = index + 1 Loop Return True End Function ''' <summary> ''' Check if a string is a valid component of the root namespace. We use the usual ''' VB identifier rules, but don't check for keywords (this is the same as previous versions). ''' </summary> Private Function IsValidRootNamespaceComponent(name As String, start As Integer, [end] As Integer, allowEscaping As Boolean) As Boolean Debug.Assert(name IsNot Nothing) ' Empty string is not valid. If start = [end] Then Return False End If Dim lastIdentifierCharacterIndex As Integer = [end] - 1 If allowEscaping AndAlso SyntaxFacts.ReturnFullWidthOrSelf(name(start)) = SyntaxFacts.FULLWIDTH_LEFT_SQUARE_BRACKET Then If SyntaxFacts.ReturnFullWidthOrSelf(name(lastIdentifierCharacterIndex)) <> SyntaxFacts.FULLWIDTH_RIGHT_SQUARE_BRACKET Then Return False End If Return IsValidRootNamespaceComponent(name, start + 1, lastIdentifierCharacterIndex, allowEscaping:=False) End If If Not SyntaxFacts.IsIdentifierStartCharacter(name(start)) Then Return False End If ' an identifier starting with an underscore must at least consist of two characters If ([end] - start) = 1 AndAlso SyntaxFacts.ReturnFullWidthOrSelf(name(start)) = SyntaxFacts.FULLWIDTH_LOW_LINE Then Return False End If For i = start + 1 To lastIdentifierCharacterIndex If Not SyntaxFacts.IsIdentifierPartCharacter(name(i)) Then Return False End If Next Return True End Function End Module End Namespace