Example #1
0
        private static CsMember parseMemberDeclarationComplexCase(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j, int afterModifiers, bool isEvent, CsTypeName type, string name, int startIndex)
        {
            // If it's "(", it's a method.
            // If it's "<", it may be a generic method, or it may be a property, event or (possibly generic) method that explicitly implements an interface member from a generic interface.
            // If it's ".", it is a property, event or (possibly generic) method that explicitly implements an interface member.

            List<CsGenericTypeParameter> genericTypeParameters = null;
            CsConcreteTypeName implementsFrom = null;

            if (tok[j].IsBuiltin("<") || tok[j].IsBuiltin("."))
            {
                // In the case that this might be an explicit interface implementation, try to parse the interface type.
                j--;
                var ty = (CsConcreteTypeName) parseTypeName(tok, ref j, typeIdentifierFlags.Lenient).Item1;

                if (tok[j].IsBuiltin(".") && tok[j + 1].IsBuiltin("this"))
                {
                    // It's an explicit implementation for an indexed property
                    var prop = new CsIndexedProperty { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs, ImplementsFrom = ty };
                    parseModifiers(prop, tok, ref i);
                    if (i != afterModifiers)
                        throw new ParseException("The modifier '{0}' is not valid for indexed properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
                    i = j + 2;
                    if (!tok[i].IsBuiltin("["))
                        throw new ParseException("'[' expected.", tok[j].StartIndex);
                    prop.Parameters = parseParameterList(tok, ref i);
                    parsePropertyBody(prop, tok, ref i);
                    prop.EndIndex = tok[i - 1].EndIndex;
                    return prop;
                }

                // Apart from 'this', no builtins (keywords) are allowed
                if (!(ty.Parts[ty.Parts.Count - 1] is CsNameIdentifier))
                    throw new ParseException("Identifier expected instead of '{0}'.".Fmt(ty.Parts[ty.Parts.Count - 1]), tok[j - 1].StartIndex);

                // The following cases are still possible:
                // ① a generic method declaration without custom attributes, e.g.
                //              void MyMethod<T>() { }
                //              In this case, ‘ty’ contains ‘MyMethod<T>’ as if T were a generic type argument instead of a generic type parameter declaration, and j points at ‘(’.
                // ② a generic method declaration with custom attributes, e.g.
                //              void MyMethod<[Foo] T>() { }
                //              In this case, ‘ty’ contains just ‘MyMethod’ and j points at ‘<’.
                // ③ an explicit interface implementation, e.g.
                //              a. void IMyInterface.MyMethod() { }                         // non-generic method
                //              b. void IMyInterface.MyMethod<T>() { }                  // generic method without custom attributes
                //              c. void IMyInterface.MyMethod<[Foo] T>() { }        // generic method with custom attributes
                //              d. int IMyInterface.MyProperty { get { ... } }                                                         // property
                //              e. event EventHandler IMyInterface.MyEvent { add { ... } remove { ... } }       // event
                //              The interface could be anything, including namespaces, nested types and generic type arguments
                //              (e.g. MyNamespace.IMyInterface<List<int>.Enumerator>).

                // In all cases, ‘ty’ contains not just the interface name, but also *at least* the member name and *at most* the member name plus its generic parameters.
                // Therefore, either way, remove it from ‘ty’ to get the genuine interface name.
                var lastPart = (CsNameIdentifier) ty.Parts[ty.Parts.Count - 1];
                ty.Parts.RemoveAt(ty.Parts.Count - 1);

                if (lastPart.GenericTypeArguments != null)
                {
                    // If the last part has generic type “arguments”, then we are in case ① or ③b.
                    // In that case, we need to convert those “arguments” into generic parameters.
                    genericTypeParameters = new List<CsGenericTypeParameter>();
                    foreach (var g in lastPart.GenericTypeArguments)
                    {
                        var single = g.GetSingleIdentifier();
                        if (single == null)
                            throw new ParseException("Invalid generic type parameter declaration.", tok[j].StartIndex);
                        genericTypeParameters.Add(new CsGenericTypeParameter { StartIndex = g.StartIndex, EndIndex = g.EndIndex, Name = single });
                    }
                }
                else if (tok[j].IsBuiltin("<"))
                {
                    // The last part didn’t have generic type arguments, and we are on a “<” token, so we are in case ② or ③c.
                    genericTypeParameters = parseGenericTypeParameterList(tok, ref j);
                }

                // The “name” that was passed in was the first token after the return type, so it would have been
                // set to the first identifier in the interface name, which is of course wrong. Fix it now that we know the real name.
                name = lastPart.Name;

                // If “ty” has any parts left, then that’s the interface name, otherwise this wasn’t an explicit interface declaration in the first place.
                implementsFrom = ty.Parts.Count == 0 ? null : ty;

                if (tok[j].IsBuiltin("{"))
                {
                    // Case ③d and ③e: an explicitly-implemented property or an event.
                    // It must be an explicit implementation because normal properties/events are already handled by parseMemberDeclaration().
                    // Explicitly-implemented events must have a body with add/remove methods; they can’t be events with the fields syntax, e.g.
                    //      event EventHandler IMyInterface.MyEvent = null;   // ✗ not allowed
                    if (genericTypeParameters != null)
                        throw new ParseException("Properties and events cannot be generic.", tok[j].StartIndex);
                    if (isEvent)
                    {
                        var nameExpr = new CsNameAndExpression
                        {
                            // We are assuming that after the modifiers comes the “event” keyword and after that, the name
                            StartIndex = tok[afterModifiers + 1].StartIndex,
                            EndIndex = tok[afterModifiers + 1].EndIndex,
                            Name = name
                        };
                        var evnt = new CsEvent
                        {
                            StartIndex = startIndex,
                            Type = type,
                            NamesAndInitializers = new List<CsNameAndExpression> { nameExpr },
                            CustomAttributes = customAttribs,
                            ImplementsFrom = implementsFrom
                        };
                        parseModifiers(evnt, tok, ref i);
                        if (i != afterModifiers)
                            throw new ParseException("The modifier '{0}' is not valid for events.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
                        i = j;
                        parseEventBody(evnt, tok, ref i);
                        evnt.EndIndex = tok[i - 1].EndIndex;
                        return evnt;
                    }
                    else
                    {
                        var prop = new CsProperty { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom };
                        parseModifiers(prop, tok, ref i);
                        if (i != afterModifiers)
                            throw new ParseException("The modifier '{0}' is not valid for properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
                        i = j;
                        parsePropertyBody(prop, tok, ref i);
                        prop.EndIndex = tok[i - 1].EndIndex;
                        return prop;
                    }
                }
                else if (isEvent && (tok[j].IsBuiltin(";") || tok[j].IsBuiltin("=")))
                {
                    // It’s an event without a body — not allowed for explicit interface implementations
                    throw new ParseException("Events that explicitly implement an interface event must have add/remove methods.", tok[i].StartIndex);
                }
            }

            // We’ve taken care of explicitly-implemented properties, indexers and events. The only case left is that it must be a method.
            // implementsFrom and genericTypeParameters are fully populated.
            CsMethod meth = new CsMethod
            {
                StartIndex = startIndex,
                Type = type,
                Name = name,
                CustomAttributes = customAttribs,
                ImplementsFrom = implementsFrom,
                GenericTypeParameters = genericTypeParameters
            };
            parseModifiers(meth, tok, ref i);
            if (i != afterModifiers)
                throw new ParseException("The modifier '{0}' is not valid for methods.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
            i = j;
            if (!tok[i].IsBuiltin("("))
                throw new ParseException("'(' expected.", tok[i].StartIndex, meth);
            try { meth.Parameters = parseParameterList(tok, ref i); }
            catch (ParseException e)
            {
                if (e.IncompleteResult is List<CsParameter>)
                    meth.Parameters = (List<CsParameter>) e.IncompleteResult;
                throw new ParseException(e.Message, e.Index, meth, e);
            }
            if (tok[i].IsIdentifier("where"))
            {
                try { meth.GenericTypeConstraints = parseGenericTypeConstraints(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is Dictionary<string, List<CsGenericTypeConstraint>>)
                        meth.GenericTypeConstraints = (Dictionary<string, List<CsGenericTypeConstraint>>) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, meth, e);
                }
            }
            if (tok[i].IsBuiltin(";"))
                i++;
            else if (tok[i].IsBuiltin("{"))
            {
                try { meth.MethodBody = parseBlock(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsBlock)
                        meth.MethodBody = (CsBlock) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, meth, e);
                }
            }
            else
                throw new ParseException(@"';', '{' or 'where' expected.", tok[i].StartIndex, meth);
            meth.EndIndex = tok[i - 1].EndIndex;
            return meth;
        }
Example #2
0
        private static object parseMemberDeclaration(TokenJar tok, ref int i, bool returnAssemblyAndModuleCustomAttributes)
        {
            var startIndex = tok[i].StartIndex;
            var customAttribs = new List<CsCustomAttributeGroup>();
            while (tok[i].IsBuiltin("["))
            {
                var k = i;
                var attr = parseCustomAttributeGroup(tok, ref i);
                if (returnAssemblyAndModuleCustomAttributes && (attr.Location == CustomAttributeLocation.Assembly || attr.Location == CustomAttributeLocation.Module))
                {
                    if (customAttribs.Count > 0)
                        throw new ParseException(@"Assembly or module custom attribute not allowed after other custom attributes.", tok[k].StartIndex);
                    return attr;
                }
                customAttribs.Add(attr);
            }

            if (tok[i].IsBuiltin("using"))
            {
                if (customAttribs.Count > 0)
                    throw new ParseException("'using' directives cannot have custom attributes.", startIndex);
                return parseUsingDeclaration(tok, ref i);
            }
            if (tok[i].IsBuiltin("namespace"))
            {
                if (customAttribs.Count > 0)
                    throw new ParseException("Namespaces cannot have custom attributes.", startIndex);
                return parseNamespace(tok, ref i);
            }

            var j = i;
            var modifiers = new[] { "abstract", "const", "extern", "internal", "new", "override", "partial", "private", "protected", "public", "readonly", "sealed", "static", "unsafe", "virtual", "volatile" };
            while ((tok[j].Type == TokenType.Builtin || tok[j].Type == TokenType.Identifier) && modifiers.Contains(tok[j].TokenStr))
                j++;
            if (tok[j].IsBuiltin("class") || tok[j].IsBuiltin("struct") || tok[j].IsBuiltin("interface"))
            {
                return parseClassStructOrInterfaceDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].IsBuiltin("delegate"))
            {
                return parseDelegateDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].IsBuiltin("enum"))
            {
                return parseEnumDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].Type == TokenType.Identifier && tok[j + 1].IsBuiltin("("))
            {
                return parseConstructorDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].IsBuiltin("~") && tok[j + 1].Type == TokenType.Identifier && tok[j + 2].IsBuiltin("("))
            {
                return parseDestructorDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].IsBuiltin("implicit") || tok[j].IsBuiltin("explicit"))
            {
                return parseCastOperatorOverloadDeclaration(tok, ref i, customAttribs, j);
            }
            else if (tok[j].IsBuiltin("operator"))
            {
                throw new ParseException("You must specify either 'implicit operator', 'explicit operator', or a return type before the 'operator' keyword.", tok[j].StartIndex);
            }
            else
            {
                var afterModifiers = j;

                // It could be a field, a method, an operator overload, a property or an event
                var isEvent = false;
                if (tok[j].IsBuiltin("event"))
                {
                    isEvent = true;
                    j++;
                }

                var prevIndex = tok[j].StartIndex;
                CsTypeName type;
                try
                {
                    type = parseTypeName(tok, ref j, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays).Item1;
                }
                catch (ParseException e)
                {
                    if (e.Index == prevIndex)
                        throw new ParseException("'class', 'struct', 'interface', 'enum', 'delegate', 'event', constructor or destructor declaration, 'implicit operator', 'explicit operator', or type expected.", tok[j].StartIndex);
                    throw;
                }

                if (tok[j].IsBuiltin("this") && !isEvent)
                {
                    // Indexed property
                    var prop = new CsIndexedProperty { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs };
                    parseModifiers(prop, tok, ref i);
                    if (i != afterModifiers)
                        throw new ParseException("The modifier '{0}' is not valid for indexed properties.".Fmt(tok[i].TokenStr), startIndex);
                    i = j + 1;
                    if (!tok[i].IsBuiltin("["))
                        throw new ParseException("'[' expected.", tok[j].StartIndex);
                    prop.Parameters = parseParameterList(tok, ref i);
                    parsePropertyBody(prop, tok, ref i);
                    prop.EndIndex = tok[i - 1].EndIndex;
                    return prop;
                }

                if (tok[j].IsBuiltin("operator") && !isEvent)
                    return parseOperatorOverloadDeclaration(tok, ref i, customAttribs, j, afterModifiers, type);

                string name = tok[j].Identifier(isEvent ? "Identifier expected." : "Identifier, 'this' or 'operator' expected.");
                j++;

                if (tok[j].IsBuiltin("{"))
                {
                    if (isEvent)
                    {
                        var evnt = new CsEvent
                        {
                            StartIndex = startIndex,
                            Type = type,
                            NamesAndInitializers = new List<CsNameAndExpression> { new CsNameAndExpression { StartIndex = tok[afterModifiers].StartIndex, EndIndex = tok[afterModifiers].EndIndex, Name = name } },
                            CustomAttributes = customAttribs
                        };
                        parseModifiers(evnt, tok, ref i);
                        if (i != afterModifiers)
                            throw new ParseException("The modifier '{0}' is not valid for events.".Fmt(tok[i].TokenStr), startIndex);
                        i = j;
                        parseEventBody(evnt, tok, ref i);
                        evnt.EndIndex = tok[i - 1].EndIndex;
                        return evnt;
                    }
                    else
                    {
                        // It’s a property
                        var prop = new CsProperty { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs };
                        parseModifiers(prop, tok, ref i);
                        if (i != afterModifiers)
                            throw new ParseException("The modifier '{0}' is not valid for properties.".Fmt(tok[i].TokenStr), startIndex);
                        i = j;
                        parsePropertyBody(prop, tok, ref i);
                        prop.EndIndex = tok[i - 1].EndIndex;
                        return prop;
                    }
                }
                else if (tok[j].IsBuiltin("=") || tok[j].IsBuiltin(";") || tok[j].IsBuiltin(","))
                {
                    return parseFieldOrEventDeclaration(isEvent, tok, ref i, customAttribs, afterModifiers, j, type, null, name);
                }
                else if (tok[j].IsBuiltin("(") || tok[j].IsBuiltin("<") || tok[j].IsBuiltin("."))
                {
                    // Could still be a method, event, property or indexer
                    return parseMemberDeclarationComplexCase(tok, ref i, customAttribs, j, afterModifiers, isEvent, type, name, startIndex);
                }
                else
                {
                    if (isEvent)
                        throw new ParseException("'=', ',', ';' or '{' expected.", tok[j].StartIndex);
                    else
                        throw new ParseException("For a field, '=', ',' or ';' expected. For a method, '(' or '<' expected. For a property, '{' expected.", tok[j].StartIndex);
                }
            }
        }