private bool IsPropertyAccessor(Declaration item, ContextAccessorType accessorType, Declaration localScope, bool isAssignmentTarget = false)
        {
            var isProperty = item.DeclarationType.HasFlag(DeclarationType.Property);

            if (!isProperty)
            {
                return(false);
            }

            if (item.Equals(localScope) && item.DeclarationType == DeclarationType.PropertyGet)
            {
                // we're resolving the getter's return value assignment
                return(true);
            }
            if (item.Equals(localScope))
            {
                // getter can't reference setter.. right?
                return(false);
            }

            return((accessorType == ContextAccessorType.AssignValue &&
                    item.DeclarationType == DeclarationType.PropertyLet)
                   ||
                   (accessorType == ContextAccessorType.AssignReference &&
                    item.DeclarationType == DeclarationType.PropertySet)
                   ||
                   (accessorType == ContextAccessorType.GetValueOrReference &&
                    item.DeclarationType == DeclarationType.PropertyGet &&
                    !isAssignmentTarget));
        }
        private Declaration FindModuleScopeProcedure(string identifierName, Declaration localScope, ContextAccessorType accessorType, bool isAssignmentTarget = false)
        {
            if (localScope == null)
            {
                localScope = _currentScope;
            }

            var matches = _declarations[identifierName];

            try
            {
                return(matches.SingleOrDefault(item =>
                                               item.Project == localScope.Project &&
                                               item.ComponentName == localScope.ComponentName &&
                                               (IsProcedure(item, localScope) || IsPropertyAccessor(item, accessorType, localScope, isAssignmentTarget))));
            }
            catch (InvalidOperationException)
            {
                return(null);
            }
        }
        private Declaration ResolveInternal(VBAParser.ImplicitCallStmt_InStmtContext callSiteContext, Declaration localScope, ContextAccessorType accessorType, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
        {
            if (callSiteContext == null)
            {
                return(null);
            }

            var dictionaryCall = callSiteContext.iCS_S_DictionaryCall();
            var fieldCall      = dictionaryCall == null ? null : dictionaryCall.dictionaryCallStmt();

            return(ResolveInternal(callSiteContext.iCS_S_VariableOrProcedureCall(), localScope, accessorType, hasExplicitLetStatement, isAssignmentTarget)
                   ?? ResolveInternal(callSiteContext.iCS_S_ProcedureOrArrayCall(), localScope, accessorType, hasExplicitLetStatement, isAssignmentTarget)
                   ?? ResolveInternal(callSiteContext.iCS_S_MembersCall(), accessorType, localScope, hasExplicitLetStatement, isAssignmentTarget)
                   ?? ResolveInternal(callSiteContext.iCS_S_DictionaryCall(), localScope, accessorType, fieldCall, hasExplicitLetStatement, isAssignmentTarget));
        }
        private Declaration ResolveInternal(VBAParser.ICS_S_MembersCallContext context, ContextAccessorType accessorType, Declaration localScope = null, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
        {
            if (context == null)
            {
                return(null);
            }

            Declaration parent;

            if (_withBlockQualifiers.Any())
            {
                parent = _withBlockQualifiers.Peek();
            }
            else
            {
                if (localScope == null)
                {
                    localScope = _currentScope;
                }
                parent = ResolveInternal(context.iCS_S_ProcedureOrArrayCall(), localScope, accessorType, hasExplicitLetStatement, isAssignmentTarget)
                         ?? ResolveInternal(context.iCS_S_VariableOrProcedureCall(), localScope, accessorType, hasExplicitLetStatement, isAssignmentTarget);

                parent = ResolveType(parent);
            }

            var chainedCalls = context.iCS_S_MemberCall();
            var lastCall     = chainedCalls.Last();

            foreach (var memberCall in chainedCalls)
            {
                // if we're on the left side of an assignment, only the last memberCall is the assignment target.
                var isLast   = memberCall.Equals(lastCall);
                var accessor = isLast
                    ? accessorType
                    : ContextAccessorType.GetValueOrReference;
                var isTarget = isLast && isAssignmentTarget;

                var member = ResolveInternal(memberCall.iCS_S_ProcedureOrArrayCall(), parent, accessor, hasExplicitLetStatement, isTarget)
                             ?? ResolveInternal(memberCall.iCS_S_VariableOrProcedureCall(), parent, accessor, hasExplicitLetStatement, isTarget);

                if (member == null)
                {
                    return(null);
                }

                parent = ResolveType(member);
            }

            var fieldCall = context.dictionaryCallStmt();

            if (fieldCall == null)
            {
                return(parent);
            }

            return(ResolveInternal(fieldCall, parent, hasExplicitLetStatement, isAssignmentTarget));
        }
        private Declaration ResolveInternal(VBAParser.ICS_S_ProcedureOrArrayCallContext context, Declaration localScope, ContextAccessorType accessorType = ContextAccessorType.GetValueOrReference, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
        {
            if (context == null)
            {
                return(null);
            }

            var identifierContext = context.ambiguousIdentifier();
            var fieldCall         = context.dictionaryCallStmt();

            // todo: understand WTF [baseType] is doing in that grammar rule...

            return(ResolveInternal(identifierContext, localScope, accessorType, fieldCall, hasExplicitLetStatement, isAssignmentTarget));
        }
        private Declaration ResolveInternal(VBAParser.ICS_S_VariableOrProcedureCallContext context, Declaration localScope, ContextAccessorType accessorType = ContextAccessorType.GetValueOrReference, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
        {
            if (context == null)
            {
                return(null);
            }

            var identifierContext = context.ambiguousIdentifier();
            var fieldCall         = context.dictionaryCallStmt();

            return(ResolveInternal(identifierContext, localScope, accessorType, fieldCall, hasExplicitLetStatement, isAssignmentTarget));
        }
        private Declaration ResolveInternal(ParserRuleContext callSiteContext, Declaration localScope, ContextAccessorType accessorType = ContextAccessorType.GetValueOrReference, VBAParser.DictionaryCallStmtContext fieldCall = null, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
        {
            if (callSiteContext == null || _alreadyResolved.Contains(callSiteContext))
            {
                return(null);
            }

            if (!IdentifierContexts.Contains(callSiteContext.GetType()))
            {
                throw new ArgumentException("'" + callSiteContext.GetType().Name + "' is not an identifier context.", "callSiteContext");
            }

            if (localScope == null)
            {
                localScope = _currentScope;
            }

            var         parentContext  = callSiteContext.Parent;
            var         identifierName = callSiteContext.GetText();
            Declaration callee         = null;

            if (localScope.DeclarationType == DeclarationType.Variable)
            {
                // localScope is probably a UDT
                var udt = ResolveType(localScope);
                if (udt != null && udt.DeclarationType == DeclarationType.UserDefinedType)
                {
                    callee = _declarations[identifierName].SingleOrDefault(item => item.Context != null && item.Context.Parent == udt.Context);
                }
            }
            else
            {
                callee = FindLocalScopeDeclaration(identifierName, localScope, parentContext, isAssignmentTarget)
                         ?? FindModuleScopeProcedure(identifierName, localScope, accessorType, isAssignmentTarget)
                         ?? FindModuleScopeDeclaration(identifierName, localScope)
                         ?? FindProjectScopeDeclaration(identifierName);
            }

            if (callee == null)
            {
                // calls inside With block can still refer to identifiers in _currentScope
                localScope     = _currentScope;
                identifierName = callSiteContext.GetText();
                callee         = FindLocalScopeDeclaration(identifierName, localScope, parentContext, isAssignmentTarget)
                                 ?? FindModuleScopeProcedure(identifierName, localScope, accessorType, isAssignmentTarget)
                                 ?? FindModuleScopeDeclaration(identifierName, localScope)
                                 ?? FindProjectScopeDeclaration(identifierName);
            }

            if (callee == null)
            {
                return(null);
            }

            var reference = CreateReference(callSiteContext, callee, isAssignmentTarget, hasExplicitLetStatement);

            callee.AddReference(reference);
            _alreadyResolved.Add(reference.Context);
            _alreadyResolved.Add(callSiteContext);

            if (fieldCall != null)
            {
                return(ResolveInternal(fieldCall, callee));
            }

            return(callee);
        }