mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Many error messages in PowerShell currently use a LINQ expression to pass both the name of the error and the message through in the error processing. This PR uses the `nameof` feature to carry the error name/ID and gets rid of LINQ expression reflection, hopefully improving performance, especially in editor scenarios.
756 lines
31 KiB
C#
756 lines
31 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Globalization;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.Runtime.CompilerServices;
|
|
using Microsoft.PowerShell.Commands;
|
|
using System.IO;
|
|
|
|
namespace System.Management.Automation.Language
|
|
{
|
|
internal enum ScopeType
|
|
{
|
|
Type, // class or enum
|
|
Method, // class method
|
|
Function, // function
|
|
ScriptBlock // script or anonymous script block
|
|
}
|
|
|
|
internal class TypeLookupResult
|
|
{
|
|
public TypeLookupResult(TypeDefinitionAst type = null)
|
|
{
|
|
Type = type;
|
|
}
|
|
|
|
public TypeDefinitionAst Type { get; set; }
|
|
public List<string> ExternalNamespaces { get; set; }
|
|
|
|
public bool IsAmbiguous()
|
|
{
|
|
return (ExternalNamespaces != null && ExternalNamespaces.Count > 1);
|
|
}
|
|
}
|
|
|
|
internal class Scope
|
|
{
|
|
internal Ast _ast;
|
|
internal ScopeType _scopeType;
|
|
/// <summary>
|
|
/// TypeTable maps namespace (currently it's module name) to the types under this namespace.
|
|
/// For the types defined in the current namespace (module) we use CURRENT_NAMESPACE as a namespace.
|
|
/// </summary>
|
|
private readonly Dictionary<string, TypeLookupResult> _typeTable;
|
|
private readonly Dictionary<string, Ast> _variableTable;
|
|
|
|
internal Scope(IParameterMetadataProvider ast, ScopeType scopeType)
|
|
{
|
|
_ast = (Ast)ast;
|
|
_scopeType = scopeType;
|
|
_typeTable = new Dictionary<string, TypeLookupResult>(StringComparer.OrdinalIgnoreCase);
|
|
_variableTable = new Dictionary<string, Ast>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
internal Scope(TypeDefinitionAst typeDefinition)
|
|
{
|
|
_ast = typeDefinition;
|
|
_scopeType = ScopeType.Type;
|
|
_typeTable = new Dictionary<string, TypeLookupResult>(StringComparer.OrdinalIgnoreCase);
|
|
_variableTable = new Dictionary<string, Ast>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var member in typeDefinition.Members)
|
|
{
|
|
var propertyMember = member as PropertyMemberAst;
|
|
if (propertyMember != null)
|
|
{
|
|
// Duplicate members are an error, but we catch that later after all types
|
|
// have been resolved. We could report errors for properties here, but
|
|
// we couldn't compare methods because overloads can't be compared until types
|
|
// are resolved.
|
|
if (!_variableTable.ContainsKey(propertyMember.Name))
|
|
{
|
|
_variableTable.Add(propertyMember.Name, propertyMember);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void AddType(Parser parser, TypeDefinitionAst typeDefinitionAst)
|
|
{
|
|
TypeLookupResult result;
|
|
if (_typeTable.TryGetValue(typeDefinitionAst.Name, out result))
|
|
{
|
|
if (result.ExternalNamespaces != null)
|
|
{
|
|
// override external type by the type defined in the current namespace
|
|
result.ExternalNamespaces = null;
|
|
result.Type = typeDefinitionAst;
|
|
}
|
|
else
|
|
{
|
|
parser.ReportError(typeDefinitionAst.Extent,
|
|
nameof(ParserStrings.MemberAlreadyDefined),
|
|
ParserStrings.MemberAlreadyDefined,
|
|
typeDefinitionAst.Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_typeTable.Add(typeDefinitionAst.Name, new TypeLookupResult(typeDefinitionAst));
|
|
}
|
|
}
|
|
|
|
internal void AddTypeFromUsingModule(Parser parser, TypeDefinitionAst typeDefinitionAst, PSModuleInfo moduleInfo)
|
|
{
|
|
TypeLookupResult result;
|
|
if (_typeTable.TryGetValue(typeDefinitionAst.Name, out result))
|
|
{
|
|
if (result.ExternalNamespaces != null)
|
|
{
|
|
// override external type by the type defined in the current namespace
|
|
result.ExternalNamespaces.Add(moduleInfo.Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var newLookupEntry = new TypeLookupResult(typeDefinitionAst)
|
|
{
|
|
ExternalNamespaces = new List<string>()
|
|
};
|
|
newLookupEntry.ExternalNamespaces.Add(moduleInfo.Name);
|
|
_typeTable.Add(typeDefinitionAst.Name, newLookupEntry);
|
|
}
|
|
|
|
string fullName = SymbolResolver.GetModuleQualifiedName(moduleInfo.Name, typeDefinitionAst.Name);
|
|
if (_typeTable.TryGetValue(fullName, out result))
|
|
{
|
|
parser.ReportError(typeDefinitionAst.Extent,
|
|
nameof(ParserStrings.MemberAlreadyDefined),
|
|
ParserStrings.MemberAlreadyDefined,
|
|
fullName);
|
|
}
|
|
else
|
|
{
|
|
_typeTable.Add(fullName, new TypeLookupResult(typeDefinitionAst));
|
|
}
|
|
}
|
|
|
|
internal TypeLookupResult LookupType(TypeName typeName)
|
|
{
|
|
if (typeName.AssemblyName != null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
TypeLookupResult typeLookupResult;
|
|
_typeTable.TryGetValue(typeName.Name, out typeLookupResult);
|
|
return typeLookupResult;
|
|
}
|
|
|
|
public Ast LookupVariable(VariablePath variablePath)
|
|
{
|
|
Ast variabledefinition;
|
|
_variableTable.TryGetValue(variablePath.UserPath, out variabledefinition);
|
|
return variabledefinition;
|
|
}
|
|
}
|
|
|
|
internal class SymbolTable
|
|
{
|
|
internal readonly List<Scope> _scopes;
|
|
internal readonly Parser _parser;
|
|
|
|
internal SymbolTable(Parser parser)
|
|
{
|
|
_scopes = new List<Scope>();
|
|
_parser = parser;
|
|
}
|
|
|
|
internal void AddTypesInScope(Ast ast)
|
|
{
|
|
// On entering a scope, we first add all the types defined in this scope (but not any nested scopes)
|
|
// This way, we can support types that refer to each other, e.g.
|
|
// class C1 { [C2]$x }
|
|
// class C2 { [C1]$c1 }
|
|
var types = ast.FindAll(x => x is TypeDefinitionAst, searchNestedScriptBlocks: false);
|
|
foreach (var type in types)
|
|
{
|
|
AddType((TypeDefinitionAst)type);
|
|
}
|
|
}
|
|
|
|
internal void EnterScope(IParameterMetadataProvider ast, ScopeType scopeType)
|
|
{
|
|
var scope = new Scope(ast, scopeType);
|
|
_scopes.Add(scope);
|
|
AddTypesInScope((Ast)ast);
|
|
}
|
|
|
|
internal void EnterScope(TypeDefinitionAst typeDefinition)
|
|
{
|
|
var scope = new Scope(typeDefinition);
|
|
_scopes.Add(scope);
|
|
AddTypesInScope(typeDefinition);
|
|
}
|
|
|
|
internal void LeaveScope()
|
|
{
|
|
Diagnostics.Assert(_scopes.Count > 0, "Scope stack can't be empty when leaving a scope");
|
|
_scopes.RemoveAt(_scopes.Count - 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Type to the symbol Table.
|
|
/// </summary>
|
|
/// <param name="typeDefinitionAst"></param>
|
|
public void AddType(TypeDefinitionAst typeDefinitionAst)
|
|
{
|
|
_scopes[_scopes.Count - 1].AddType(_parser, typeDefinitionAst);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Type from the different module to the symbol Table.
|
|
/// </summary>
|
|
/// <param name="typeDefinitionAst"></param>
|
|
/// <param name="moduleInfo"></param>
|
|
public void AddTypeFromUsingModule(TypeDefinitionAst typeDefinitionAst, PSModuleInfo moduleInfo)
|
|
{
|
|
_scopes[_scopes.Count - 1].AddTypeFromUsingModule(_parser, typeDefinitionAst, moduleInfo);
|
|
}
|
|
|
|
public TypeLookupResult LookupType(TypeName typeName)
|
|
{
|
|
TypeLookupResult result = null;
|
|
for (int i = _scopes.Count - 1; i >= 0; i--)
|
|
{
|
|
result = _scopes[i].LookupType(typeName);
|
|
if (result != null)
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public Ast LookupVariable(VariablePath variablePath)
|
|
{
|
|
Ast result = null;
|
|
for (int i = _scopes.Count - 1; i >= 0; i--)
|
|
{
|
|
result = _scopes[i].LookupVariable(variablePath);
|
|
if (result != null)
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the most deep typeDefinitionAst in the current context.
|
|
/// </summary>
|
|
/// <returns>typeDefinitionAst or null, if currently not in type definition</returns>
|
|
public TypeDefinitionAst GetCurrentTypeDefinitionAst()
|
|
{
|
|
for (int i = _scopes.Count - 1; i >= 0; i--)
|
|
{
|
|
TypeDefinitionAst ast = _scopes[i]._ast as TypeDefinitionAst;
|
|
if (ast != null)
|
|
{
|
|
return ast;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public bool IsInMethodScope()
|
|
{
|
|
return _scopes[_scopes.Count - 1]._scopeType == ScopeType.Method;
|
|
}
|
|
}
|
|
|
|
internal class SymbolResolver : AstVisitor2, IAstPostVisitHandler
|
|
{
|
|
private readonly SymbolResolvePostActionVisitor _symbolResolvePostActionVisitor;
|
|
internal readonly SymbolTable _symbolTable;
|
|
internal readonly Parser _parser;
|
|
internal readonly TypeResolutionState _typeResolutionState;
|
|
|
|
[ThreadStatic]
|
|
private static PowerShell t_usingStatementResolvePowerShell;
|
|
|
|
private static PowerShell UsingStatementResolvePowerShell
|
|
{
|
|
get
|
|
{
|
|
// The goal is to re-use runspaces, because creating runspace is an expensive part in creating PowerShell instance.
|
|
if (t_usingStatementResolvePowerShell == null)
|
|
{
|
|
if (Runspace.DefaultRunspace != null)
|
|
{
|
|
t_usingStatementResolvePowerShell = PowerShell.Create(RunspaceMode.CurrentRunspace);
|
|
}
|
|
else
|
|
{
|
|
// Create empty iss and populate only commands, that we want to use.
|
|
InitialSessionState iss = InitialSessionState.Create();
|
|
iss.Commands.Add(new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), null));
|
|
var sessionStateProviderEntry = new SessionStateProviderEntry(FileSystemProvider.ProviderName, typeof(FileSystemProvider), null);
|
|
var snapin = PSSnapInReader.ReadEnginePSSnapIns().FirstOrDefault(snapIn => snapIn.Name.Equals("Microsoft.PowerShell.Core", StringComparison.OrdinalIgnoreCase));
|
|
sessionStateProviderEntry.SetPSSnapIn(snapin);
|
|
iss.Providers.Add(sessionStateProviderEntry);
|
|
t_usingStatementResolvePowerShell = PowerShell.Create(iss);
|
|
}
|
|
}
|
|
else if (Runspace.DefaultRunspace != null && t_usingStatementResolvePowerShell.Runspace != Runspace.DefaultRunspace)
|
|
{
|
|
t_usingStatementResolvePowerShell = PowerShell.Create(RunspaceMode.CurrentRunspace);
|
|
}
|
|
|
|
return t_usingStatementResolvePowerShell;
|
|
}
|
|
}
|
|
|
|
private SymbolResolver(Parser parser, TypeResolutionState typeResolutionState)
|
|
{
|
|
_symbolTable = new SymbolTable(parser);
|
|
_parser = parser;
|
|
_typeResolutionState = typeResolutionState;
|
|
_symbolResolvePostActionVisitor = new SymbolResolvePostActionVisitor { _symbolResolver = this };
|
|
}
|
|
|
|
internal static void ResolveSymbols(Parser parser, ScriptBlockAst scriptBlockAst)
|
|
{
|
|
Diagnostics.Assert(scriptBlockAst.Parent == null, "Can only resolve starting from the root");
|
|
|
|
var usingState = scriptBlockAst.UsingStatements.Count > 0
|
|
? new TypeResolutionState(TypeOps.GetNamespacesForTypeResolutionState(scriptBlockAst.UsingStatements), TypeResolutionState.emptyAssemblies)
|
|
: TypeResolutionState.GetDefaultUsingState(null);
|
|
var resolver = new SymbolResolver(parser, usingState);
|
|
resolver._symbolTable.EnterScope(scriptBlockAst, ScopeType.ScriptBlock);
|
|
scriptBlockAst.Visit(resolver);
|
|
resolver._symbolTable.LeaveScope();
|
|
|
|
Diagnostics.Assert(resolver._symbolTable._scopes.Count == 0, "Somebody missed removing a scope");
|
|
}
|
|
|
|
public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
|
|
{
|
|
_symbolTable.EnterScope(typeDefinitionAst);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)
|
|
{
|
|
_symbolTable.EnterScope(scriptBlockExpressionAst.ScriptBlock, ScopeType.ScriptBlock);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
|
|
{
|
|
if (!(functionDefinitionAst.Parent is FunctionMemberAst))
|
|
{
|
|
_symbolTable.EnterScope(functionDefinitionAst.Body, ScopeType.Function);
|
|
}
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitPropertyMember(PropertyMemberAst propertyMemberAst)
|
|
{
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)
|
|
{
|
|
_symbolTable.EnterScope(functionMemberAst.Body, ScopeType.Method);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)
|
|
{
|
|
if (_symbolTable.IsInMethodScope())
|
|
{
|
|
var targets = assignmentStatementAst.GetAssignmentTargets().ToArray();
|
|
foreach (var expressionAst in targets)
|
|
{
|
|
var expression = expressionAst;
|
|
var variableExpressionAst = expression as VariableExpressionAst;
|
|
while (variableExpressionAst == null && expression != null)
|
|
{
|
|
var convertExpressionAst = expression as ConvertExpressionAst;
|
|
if (convertExpressionAst != null)
|
|
{
|
|
expression = convertExpressionAst.Child;
|
|
variableExpressionAst = convertExpressionAst.Child as VariableExpressionAst;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (variableExpressionAst != null && variableExpressionAst.VariablePath.IsVariable)
|
|
{
|
|
var ast = _symbolTable.LookupVariable(variableExpressionAst.VariablePath);
|
|
var propertyMember = ast as PropertyMemberAst;
|
|
if (propertyMember != null)
|
|
{
|
|
if (propertyMember.IsStatic)
|
|
{
|
|
var typeAst = _symbolTable.GetCurrentTypeDefinitionAst();
|
|
Diagnostics.Assert(typeAst != null, "Method scopes can exist only inside type definitions.");
|
|
|
|
string typeString = String.Format(CultureInfo.InvariantCulture, "[{0}]::", typeAst.Name);
|
|
_parser.ReportError(variableExpressionAst.Extent,
|
|
nameof(ParserStrings.MissingTypeInStaticPropertyAssignment),
|
|
ParserStrings.MissingTypeInStaticPropertyAssignment,
|
|
typeString,
|
|
propertyMember.Name);
|
|
}
|
|
else
|
|
{
|
|
_parser.ReportError(variableExpressionAst.Extent,
|
|
nameof(ParserStrings.MissingThis),
|
|
ParserStrings.MissingThis,
|
|
"$this.",
|
|
propertyMember.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// TODO: static look for alias and function.
|
|
}
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpressionAst)
|
|
{
|
|
DispatchTypeName(typeExpressionAst.TypeName, genericArgumentCount: 0, isAttribute: false);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst)
|
|
{
|
|
DispatchTypeName(typeConstraintAst.TypeName, genericArgumentCount: 0, isAttribute: false);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolves using module to a collection of PSModuleInfos. Doesn't throw.
|
|
/// PSModuleInfo objects are returned in the right order: i.e. if multiply versions of the module
|
|
/// is presented on the system and user didn't specify version, we will return all of them, but newer one would go first.
|
|
/// </summary>
|
|
/// <param name="usingStatementAst">using statement</param>
|
|
/// <param name="exception">If exception happens, return exception object.</param>
|
|
/// <param name="wildcardCharactersUsed">
|
|
/// True if in the module name uses wildcardCharacter.
|
|
/// We don't want to resolve any wild-cards in using module.
|
|
/// </param>
|
|
/// <param name="isConstant">True if module hashtable contains constant value (it's our requirement).</param>
|
|
/// <returns>Modules, if can resolve it. null if any problems happens.</returns>
|
|
private Collection<PSModuleInfo> GetModulesFromUsingModule(UsingStatementAst usingStatementAst, out Exception exception, out bool wildcardCharactersUsed, out bool isConstant)
|
|
{
|
|
exception = null;
|
|
wildcardCharactersUsed = false;
|
|
isConstant = true;
|
|
|
|
// fullyQualifiedName can be string or hashtable
|
|
object fullyQualifiedName;
|
|
if (usingStatementAst.ModuleSpecification != null)
|
|
{
|
|
object resultObject;
|
|
if (!IsConstantValueVisitor.IsConstant(usingStatementAst.ModuleSpecification, out resultObject, forAttribute: false, forRequires: true))
|
|
{
|
|
isConstant = false;
|
|
return null;
|
|
}
|
|
|
|
var hashtable = resultObject as System.Collections.Hashtable;
|
|
var ms = new ModuleSpecification();
|
|
exception = ModuleSpecification.ModuleSpecificationInitHelper(ms, hashtable);
|
|
if (exception != null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (WildcardPattern.ContainsWildcardCharacters(ms.Name))
|
|
{
|
|
wildcardCharactersUsed = true;
|
|
return null;
|
|
}
|
|
|
|
fullyQualifiedName = ms;
|
|
}
|
|
else
|
|
{
|
|
string fullyQualifiedNameStr = usingStatementAst.Name.Value;
|
|
|
|
if (WildcardPattern.ContainsWildcardCharacters(fullyQualifiedNameStr))
|
|
{
|
|
wildcardCharactersUsed = true;
|
|
return null;
|
|
}
|
|
|
|
// case 1: relative path. Relative for file in the same folder should include .\
|
|
bool isPath = fullyQualifiedNameStr.Contains(@"\");
|
|
if (isPath && !LocationGlobber.IsAbsolutePath(fullyQualifiedNameStr))
|
|
{
|
|
string rootPath = Path.GetDirectoryName(_parser._fileName);
|
|
if (rootPath != null)
|
|
{
|
|
fullyQualifiedNameStr = Path.Combine(rootPath, fullyQualifiedNameStr);
|
|
}
|
|
}
|
|
|
|
// case 2: Module by name
|
|
// case 3: Absolute Path
|
|
// We don't need to do anything for these cases, FullyQualifiedName already handle it.
|
|
|
|
fullyQualifiedName = fullyQualifiedNameStr;
|
|
}
|
|
|
|
var commandInfo = new CmdletInfo("Get-Module", typeof(GetModuleCommand));
|
|
// TODO(sevoroby): we should consider an async call with cancellation here.
|
|
UsingStatementResolvePowerShell.Commands.Clear();
|
|
try
|
|
{
|
|
return UsingStatementResolvePowerShell.AddCommand(commandInfo)
|
|
.AddParameter("FullyQualifiedName", fullyQualifiedName)
|
|
.AddParameter("ListAvailable", true)
|
|
.Invoke<PSModuleInfo>();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
exception = e;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override AstVisitAction VisitUsingStatement(UsingStatementAst usingStatementAst)
|
|
{
|
|
if (usingStatementAst.UsingStatementKind == UsingStatementKind.Module)
|
|
{
|
|
Exception exception;
|
|
bool wildcardCharactersUsed;
|
|
bool isConstant;
|
|
var moduleInfo = GetModulesFromUsingModule(usingStatementAst, out exception, out wildcardCharactersUsed, out isConstant);
|
|
if (!isConstant)
|
|
{
|
|
_parser.ReportError(usingStatementAst.Extent,
|
|
nameof(ParserStrings.RequiresArgumentMustBeConstant),
|
|
ParserStrings.RequiresArgumentMustBeConstant);
|
|
}
|
|
else if (exception != null)
|
|
{
|
|
// we re-using RequiresModuleInvalid string, semantic is very similar so it's fine to do that.
|
|
_parser.ReportError(usingStatementAst.Extent,
|
|
nameof(ParserStrings.RequiresModuleInvalid),
|
|
ParserStrings.RequiresModuleInvalid,
|
|
exception.Message);
|
|
}
|
|
else if (wildcardCharactersUsed)
|
|
{
|
|
_parser.ReportError(usingStatementAst.Extent,
|
|
nameof(ParserStrings.WildCardModuleNameError),
|
|
ParserStrings.WildCardModuleNameError);
|
|
}
|
|
else if (moduleInfo != null && moduleInfo.Count > 0)
|
|
{
|
|
// it's ok, if we get more then one module. They are already sorted in the right order
|
|
// we just need to use the first one
|
|
|
|
// We must add the same objects (in sense of object refs) to usingStatementAst typeTable and to symbolTable.
|
|
// Later, this same TypeDefinitionAsts would be used in DefineTypes(), by the module, where it was imported from at compile time.
|
|
var exportedTypes = usingStatementAst.DefineImportedModule(moduleInfo[0]);
|
|
foreach (var typePairs in exportedTypes)
|
|
{
|
|
_symbolTable.AddTypeFromUsingModule(typePairs.Value, moduleInfo[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if there is no exception, but we didn't find the module then it's not present
|
|
string moduleText = usingStatementAst.Name != null ? usingStatementAst.Name.Value : usingStatementAst.ModuleSpecification.Extent.Text;
|
|
_parser.ReportError(usingStatementAst.Extent,
|
|
nameof(ParserStrings.ModuleNotFoundDuringParse),
|
|
ParserStrings.ModuleNotFoundDuringParse,
|
|
moduleText);
|
|
}
|
|
}
|
|
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
public override AstVisitAction VisitAttribute(AttributeAst attributeAst)
|
|
{
|
|
DispatchTypeName(attributeAst.TypeName, genericArgumentCount: 0, isAttribute: true);
|
|
return AstVisitAction.Continue;
|
|
}
|
|
|
|
private bool DispatchTypeName(ITypeName type, int genericArgumentCount, bool isAttribute)
|
|
{
|
|
RuntimeHelpers.EnsureSufficientExecutionStack();
|
|
var typeName = type as TypeName;
|
|
if (typeName != null)
|
|
{
|
|
return VisitTypeName(typeName, genericArgumentCount, isAttribute);
|
|
}
|
|
else
|
|
{
|
|
var arrayTypeName = type as ArrayTypeName;
|
|
if (arrayTypeName != null)
|
|
{
|
|
return VisitArrayTypeName(arrayTypeName);
|
|
}
|
|
else
|
|
{
|
|
var genericTypeName = type as GenericTypeName;
|
|
if (genericTypeName != null)
|
|
{
|
|
return VisitGenericTypeName(genericTypeName);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool VisitArrayTypeName(ArrayTypeName arrayTypeName)
|
|
{
|
|
bool resolved = DispatchTypeName(arrayTypeName.ElementType, genericArgumentCount: 0, isAttribute: false);
|
|
if (resolved)
|
|
{
|
|
var resolvedType = arrayTypeName.GetReflectionType();
|
|
TypeCache.Add(arrayTypeName, _typeResolutionState, resolvedType);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
private bool VisitTypeName(TypeName typeName, int genericArgumentCount, bool isAttribute)
|
|
{
|
|
var classDefn = _symbolTable.LookupType(typeName);
|
|
|
|
if (classDefn != null && classDefn.IsAmbiguous())
|
|
{
|
|
_parser.ReportError(typeName.Extent,
|
|
nameof(ParserStrings.AmbiguousTypeReference),
|
|
ParserStrings.AmbiguousTypeReference,
|
|
typeName.Name,
|
|
GetModuleQualifiedName(classDefn.ExternalNamespaces[0], typeName.Name),
|
|
GetModuleQualifiedName(classDefn.ExternalNamespaces[1], typeName.Name));
|
|
}
|
|
else if (classDefn != null && genericArgumentCount == 0)
|
|
{
|
|
typeName.SetTypeDefinition(classDefn.Type);
|
|
}
|
|
else
|
|
{
|
|
Exception e;
|
|
TypeResolutionState trs = genericArgumentCount > 0 || isAttribute
|
|
? new TypeResolutionState(_typeResolutionState, genericArgumentCount, isAttribute)
|
|
: _typeResolutionState;
|
|
|
|
var type = TypeResolver.ResolveTypeNameWithContext(typeName, out e, null, trs);
|
|
if (type == null)
|
|
{
|
|
if (_symbolTable.GetCurrentTypeDefinitionAst() != null)
|
|
{
|
|
// [ordered] is an attribute, but it's looks like a type constraint.
|
|
if (!typeName.FullName.Equals(LanguagePrimitives.OrderedAttribute, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string errorId;
|
|
string errorMsg;
|
|
if (isAttribute)
|
|
{
|
|
errorId = nameof(ParserStrings.CustomAttributeTypeNotFound);
|
|
errorMsg = ParserStrings.CustomAttributeTypeNotFound;
|
|
}
|
|
else
|
|
{
|
|
errorId = nameof(ParserStrings.TypeNotFound);
|
|
errorMsg = ParserStrings.TypeNotFound;
|
|
}
|
|
_parser.ReportError(typeName.Extent, errorId, errorMsg, typeName.Name);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
((ISupportsTypeCaching)typeName).CachedType = type;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool VisitGenericTypeName(GenericTypeName genericTypeName)
|
|
{
|
|
var foundType = TypeCache.Lookup(genericTypeName, _typeResolutionState);
|
|
if (foundType != null)
|
|
{
|
|
((ISupportsTypeCaching)genericTypeName).CachedType = foundType;
|
|
return true;
|
|
}
|
|
|
|
bool resolved = true;
|
|
resolved &= DispatchTypeName(genericTypeName.TypeName, genericTypeName.GenericArguments.Count, isAttribute: false);
|
|
foreach (var typeArg in genericTypeName.GenericArguments)
|
|
{
|
|
resolved &= DispatchTypeName(typeArg, genericArgumentCount: 0, isAttribute: false);
|
|
}
|
|
|
|
if (resolved)
|
|
{
|
|
var resolvedType = genericTypeName.GetReflectionType();
|
|
TypeCache.Add(genericTypeName, _typeResolutionState, resolvedType);
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
public void PostVisit(Ast ast)
|
|
{
|
|
ast.Accept(_symbolResolvePostActionVisitor);
|
|
}
|
|
|
|
internal static string GetModuleQualifiedName(string namespaceName, string typeName)
|
|
{
|
|
const char NAMESPACE_SEPARATOR = '.';
|
|
return namespaceName + NAMESPACE_SEPARATOR + typeName;
|
|
}
|
|
}
|
|
|
|
internal class SymbolResolvePostActionVisitor : DefaultCustomAstVisitor2
|
|
{
|
|
internal SymbolResolver _symbolResolver;
|
|
|
|
public override object VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
|
|
{
|
|
if (!(functionDefinitionAst.Parent is FunctionMemberAst))
|
|
{
|
|
_symbolResolver._symbolTable.LeaveScope();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public override object VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)
|
|
{
|
|
_symbolResolver._symbolTable.LeaveScope();
|
|
return null;
|
|
}
|
|
|
|
public override object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)
|
|
{
|
|
_symbolResolver._symbolTable.LeaveScope();
|
|
return null;
|
|
}
|
|
|
|
public override object VisitFunctionMember(FunctionMemberAst functionMemberAst)
|
|
{
|
|
_symbolResolver._symbolTable.LeaveScope();
|
|
return null;
|
|
}
|
|
}
|
|
}
|