// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp.Resolver { /// /// Traverses the DOM and resolves every expression. /// public class ResolveVisitor : AbstractDomVisitor { static readonly ResolveResult errorResult = new ErrorResolveResult(SharedTypes.UnknownType); readonly CSharpResolver resolver; readonly ParsedFile parsedFile; readonly Dictionary cache = new Dictionary(); /// /// Set this property to false to skip resolving all sub expressions. /// public bool FullyResolveSubExpressions { get; set; } public ResolveVisitor(CSharpResolver resolver, ParsedFile parsedFile) { if (resolver == null) throw new ArgumentNullException("resolver"); this.resolver = resolver; this.parsedFile = parsedFile; this.FullyResolveSubExpressions = true; } public ResolveResult Resolve(INode node) { if (node == null) return errorResult; ResolveResult result; if (!cache.TryGetValue(node, out result)) { result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult; } return result; } public ResolveResult GetResolveResult(INode node) { ResolveResult result; if (cache.TryGetValue(node, out result)) return result; else return null; } #region Track UsingScope public override ResolveResult VisitCompilationUnit(CompilationUnit unit, object data) { UsingScope previousUsingScope = resolver.UsingScope; try { if (parsedFile != null) resolver.UsingScope = parsedFile.RootUsingScope; return base.VisitCompilationUnit(unit, data); } finally { resolver.UsingScope = previousUsingScope; } } public override ResolveResult VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) { UsingScope previousUsingScope = resolver.UsingScope; try { if (parsedFile != null) { resolver.UsingScope = parsedFile.GetUsingScope(namespaceDeclaration.StartLocation); } base.VisitNamespaceDeclaration(namespaceDeclaration, data); return new NamespaceResolveResult(resolver.UsingScope.NamespaceName); } finally { resolver.UsingScope = previousUsingScope; } } #endregion #region Track CurrentTypeDefinition public override ResolveResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) { ITypeDefinition previousTypeDefinition = resolver.CurrentTypeDefinition; try { ITypeDefinition newTypeDefinition = null; if (resolver.CurrentTypeDefinition != null) { foreach (ITypeDefinition innerClass in resolver.CurrentTypeDefinition.InnerClasses) { if (innerClass.Region.IsInside(typeDeclaration.StartLocation.Line, typeDeclaration.StartLocation.Column)) { newTypeDefinition = innerClass; break; } } } else if (parsedFile != null) { newTypeDefinition = parsedFile.GetTopLevelTypeDefinition(typeDeclaration.StartLocation); } if (newTypeDefinition != null) resolver.CurrentTypeDefinition = newTypeDefinition; base.VisitTypeDeclaration(typeDeclaration, data); return newTypeDefinition != null ? new TypeResolveResult(newTypeDefinition) : errorResult; } finally { resolver.CurrentTypeDefinition = previousTypeDefinition; } } #endregion #region Track CurrentMember public override ResolveResult VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) { if (FullyResolveSubExpressions) { ResolveType(fieldDeclaration.ReturnType); foreach (AttributeSection attr in fieldDeclaration.Attributes) Resolve(attr); } if (fieldDeclaration.Variables.Count() == 1) { return Resolve(fieldDeclaration.Variables.Single()); } else { foreach (VariableInitializer vi in fieldDeclaration.Variables) Resolve(vi); return null; } } public override ResolveResult VisitVariableInitializer(VariableInitializer variableInitializer, object data) { if (variableInitializer.Parent is FieldDeclaration) { try { if (resolver.CurrentTypeDefinition != null) { resolver.CurrentMember = resolver.CurrentTypeDefinition.Fields.FirstOrDefault(f => f.Region.IsInside(variableInitializer.StartLocation)); } if (FullyResolveSubExpressions) Resolve(variableInitializer.Initializer); if (resolver.CurrentMember != null) return new MemberResolveResult(resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); else return errorResult; } finally{ resolver.CurrentMember = null; } } else { return base.VisitVariableInitializer(variableInitializer, data); } } ResolveResult VisitMethodMember(AbstractMemberBase member, object data) { try { if (resolver.CurrentTypeDefinition != null) { resolver.CurrentMember = resolver.CurrentTypeDefinition.Methods.FirstOrDefault(m => m.Region.IsInside(member.StartLocation)); } VisitChildren(member, data); if (resolver.CurrentMember != null) return new MemberResolveResult(resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); else return errorResult; } finally { resolver.CurrentMember = null; } } public override ResolveResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) { return VisitMethodMember(methodDeclaration, data); } public override ResolveResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) { return VisitMethodMember(operatorDeclaration, data); } public override ResolveResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) { return VisitMethodMember(constructorDeclaration, data); } public override ResolveResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data) { return VisitMethodMember(destructorDeclaration, data); } #endregion #region Track CheckForOverflow public override ResolveResult VisitCheckedExpression(CheckedExpression checkedExpression, object data) { bool oldCheckForOverflow = resolver.CheckForOverflow; try { resolver.CheckForOverflow = true; return checkedExpression.Expression.AcceptVisitor(this, data); } finally { resolver.CheckForOverflow = oldCheckForOverflow; } } public override ResolveResult VisitUncheckedExpression(UncheckedExpression uncheckedExpression, object data) { bool oldCheckForOverflow = resolver.CheckForOverflow; try { resolver.CheckForOverflow = false; return uncheckedExpression.Expression.AcceptVisitor(this, data); } finally { resolver.CheckForOverflow = oldCheckForOverflow; } } public override ResolveResult VisitCheckedStatement(CheckedStatement checkedStatement, object data) { bool oldCheckForOverflow = resolver.CheckForOverflow; try { resolver.CheckForOverflow = true; return base.VisitCheckedStatement(checkedStatement, data); } finally { resolver.CheckForOverflow = oldCheckForOverflow; } } public override ResolveResult VisitUncheckedStatement(UncheckedStatement uncheckedStatement, object data) { bool oldCheckForOverflow = resolver.CheckForOverflow; try { resolver.CheckForOverflow = true; return base.VisitUncheckedStatement(uncheckedStatement, data); } finally { resolver.CheckForOverflow = oldCheckForOverflow; } } #endregion #region VisitChildren protected override ResolveResult VisitChildren(INode node, object data) { // this method is used to iterate through all children if (FullyResolveSubExpressions) { INode child = node.FirstChild; while (child != null) { ResolveResult rr = child.AcceptVisitor (this, data); if (rr != null) cache[child] = rr; child = child.NextSibling; } } return null; } #endregion #region Visit Expressions static bool IsTargetOfInvocation(INode node) { InvocationExpression ie = node.Parent as InvocationExpression; return ie != null && ie.Target == node; } IType ResolveType(INode node) { return SharedTypes.UnknownType; } public override ResolveResult VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) { throw new NotImplementedException(); } public override ResolveResult VisitArgListExpression(ArgListExpression argListExpression, object data) { return new ResolveResult(resolver.Context.GetClass(typeof(RuntimeArgumentHandle)) ?? SharedTypes.UnknownType); } public override ResolveResult VisitArrayObjectCreateExpression(ArrayObjectCreateExpression arrayObjectCreateExpression, object data) { throw new NotImplementedException(); } public override ResolveResult VisitAsExpression(AsExpression asExpression, object data) { if (FullyResolveSubExpressions) Resolve(asExpression.Expression); return new ResolveResult(ResolveType(asExpression.TypeReference)); } public override ResolveResult VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) { ResolveResult left = Resolve(assignmentExpression.Left); if (FullyResolveSubExpressions) { Resolve(assignmentExpression.Right); } return new ResolveResult(left.Type); } public override ResolveResult VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, object data) { return resolver.ResolveBaseReference(); } public override ResolveResult VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) { ResolveResult left = Resolve(binaryOperatorExpression.Left); ResolveResult right = Resolve(binaryOperatorExpression.Right); return resolver.ResolveBinaryOperator(binaryOperatorExpression.BinaryOperatorType, left, right); } public override ResolveResult VisitCastExpression(CastExpression castExpression, object data) { return resolver.ResolveCast(ResolveType(castExpression.CastTo), Resolve(castExpression.Expression)); } public override ResolveResult VisitConditionalExpression(ConditionalExpression conditionalExpression, object data) { if (FullyResolveSubExpressions) Resolve(conditionalExpression.Condition); return resolver.ResolveConditional(Resolve(conditionalExpression.TrueExpression), Resolve(conditionalExpression.FalseExpression)); } public override ResolveResult VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data) { return new ConstantResolveResult(ResolveType(defaultValueExpression.TypeReference), null); } public override ResolveResult VisitDirectionExpression(DirectionExpression directionExpression, object data) { ResolveResult rr = Resolve(directionExpression.Expression); return new ByReferenceResolveResult(rr.Type, directionExpression.FieldDirection == FieldDirection.Out); } public override ResolveResult VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) { // TODO: type arguments? return resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList.Instance, IsTargetOfInvocation(identifierExpression)); } ResolveResult[] GetArguments(IEnumerable argumentExpressions, out string[] argumentNames) { argumentNames = null; // TODO: add support for named arguments ResolveResult[] arguments = new ResolveResult[argumentExpressions.Count()]; int i = 0; foreach (INode argument in argumentExpressions) { arguments[i++] = Resolve(argument); } return arguments; } public override ResolveResult VisitIndexerExpression(IndexerExpression indexerExpression, object data) { ResolveResult target = Resolve(indexerExpression.Target); string[] argumentNames; ResolveResult[] arguments = GetArguments(indexerExpression.Arguments, out argumentNames); return resolver.ResolveIndexer(target, arguments, argumentNames); } public override ResolveResult VisitInvocationExpression(InvocationExpression invocationExpression, object data) { ResolveResult target = Resolve(invocationExpression.Target); string[] argumentNames; ResolveResult[] arguments = GetArguments(invocationExpression.Arguments, out argumentNames); return resolver.ResolveInvocation(target, arguments, argumentNames); } public override ResolveResult VisitIsExpression(IsExpression isExpression, object data) { if (FullyResolveSubExpressions) { Resolve(isExpression.Expression); ResolveType(isExpression.TypeReference); } return new ResolveResult(TypeCode.Boolean.ToTypeReference().Resolve(resolver.Context)); } public override ResolveResult VisitLambdaExpression(LambdaExpression lambdaExpression, object data) { throw new NotImplementedException(); } public override ResolveResult VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) { ResolveResult target = Resolve(memberReferenceExpression.Target); List typeArgumentNodes = memberReferenceExpression.TypeArguments.ToList(); // TODO: type arguments? return resolver.ResolveMemberAccess(target, memberReferenceExpression.MemberName, EmptyList.Instance, IsTargetOfInvocation(memberReferenceExpression)); } public override ResolveResult VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, object data) { return resolver.ResolvePrimitive(null); } public override ResolveResult VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) { IType type = ResolveType(objectCreateExpression.Type); string[] argumentNames; ResolveResult[] arguments = GetArguments(objectCreateExpression.Arguments, out argumentNames); return resolver.ResolveObjectCreation(type, arguments, argumentNames); } public override ResolveResult VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) { return Resolve(parenthesizedExpression.Expression); } public override ResolveResult VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) { throw new NotImplementedException(); } public override ResolveResult VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) { return resolver.ResolvePrimitive(primitiveExpression.Value); } public override ResolveResult VisitSizeOfExpression(SizeOfExpression sizeOfExpression, object data) { return resolver.ResolveSizeOf(ResolveType(sizeOfExpression.Type)); } public override ResolveResult VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data) { if (FullyResolveSubExpressions) Resolve(stackAllocExpression.CountExpression); return new ResolveResult(new PointerType(ResolveType(stackAllocExpression.Type))); } public override ResolveResult VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) { return resolver.ResolveThisReference(); } static readonly GetClassTypeReference systemType = new GetClassTypeReference("System.Type", 0); public override ResolveResult VisitTypeOfExpression(TypeOfExpression typeOfExpression, object data) { if (FullyResolveSubExpressions) ResolveType(typeOfExpression.Type); return new ResolveResult(systemType.Resolve(resolver.Context)); } public override ResolveResult VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) { ResolveResult expr = Resolve(unaryOperatorExpression.Expression); return resolver.ResolveUnaryOperator(unaryOperatorExpression.UnaryOperatorType, expr); } #endregion public override ResolveResult VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, object data) { if (FullyResolveSubExpressions) { ResolveType(parameterDeclaration.Type); Resolve(parameterDeclaration.DefaultExpression); } IParameterizedMember pm = resolver.CurrentMember as IParameterizedMember; if (pm != null) { foreach (IParameter p in pm.Parameters) { if (p.Name == parameterDeclaration.Name) { return new VariableResolveResult(p, p.Type.Resolve(resolver.Context)); } } } return errorResult; } } }