/* * Copyright 2013-2017 consulo.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package consulo.csharp.lang.parser.decl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.csharp.lang.parser.CSharpBuilderWrapper; import consulo.csharp.lang.parser.ModifierSet; import consulo.csharp.lang.parser.exp.ExpressionParsing; import consulo.csharp.lang.parser.stmt.StatementParsing; import consulo.csharp.lang.psi.CSharpElements; import consulo.csharp.lang.psi.CSharpStubElements; import consulo.csharp.lang.psi.CSharpTokens; import com.intellij.lang.PsiBuilder; import com.intellij.openapi.util.Pair; import com.intellij.psi.tree.IElementType; import com.intellij.util.BitUtil; /** * @author VISTALL * @since 28.11.13. */ public class MethodParsing extends MemberWithBodyParsing { public static enum Target { CONSTRUCTOR, DECONSTRUCTOR, METHOD, CONVERSION_METHOD } public static void parseMethodStartAtType(@NotNull CSharpBuilderWrapper builder, @NotNull PsiBuilder.Marker marker, @NotNull ModifierSet set) { TypeInfo typeInfo = parseType(builder, STUB_SUPPORT); if(typeInfo != null) { parseMethodStartAfterType(builder, marker, typeInfo, Target.METHOD, set); } else { builder.error("Name expected"); done(marker, METHOD_DECLARATION); } } public static void parseMethodStartAfterType(@NotNull CSharpBuilderWrapper builder, @NotNull PsiBuilder.Marker marker, @Nullable TypeInfo typeInfo, @NotNull Target target, @NotNull ModifierSet set) { if(target == Target.CONSTRUCTOR || target == Target.DECONSTRUCTOR) { expectOrReportIdentifier(builder, STUB_SUPPORT); } else { if(builder.getTokenType() == OPERATOR_KEYWORD) { builder.advanceLexer(); IElementType tokenTypeGGLL = builder.getTokenTypeGGLL(); if(typeInfo != null && (typeInfo.nativeElementType == EXPLICIT_KEYWORD || typeInfo.nativeElementType == IMPLICIT_KEYWORD)) { if(parseType(builder, STUB_SUPPORT) == null) { builder.error("Type expected"); } target = Target.CONVERSION_METHOD; } else { if(OVERLOADING_OPERATORS.contains(tokenTypeGGLL)) { builder.advanceLexerGGLL(); } else { builder.error("Operator name expected"); } } } else { expectOrReportIdentifier(builder, STUB_SUPPORT); } } parseMethodStartAfterName(builder, marker, target, set); } public static void parseMethodStartAfterName(@NotNull CSharpBuilderWrapper builder, @NotNull PsiBuilder.Marker marker, @NotNull Target target, @NotNull ModifierSet set) { GenericParameterParsing.parseList(builder); if(builder.getTokenType() == LPAR) { // deconstructors dont process any parameters if(target == Target.DECONSTRUCTOR) { PsiBuilder.Marker parameterMarker = builder.mark(); builder.advanceLexer(); expect(builder, RPAR, "')' expected"); parameterMarker.done(CSharpStubElements.PARAMETER_LIST); } else { parseParameterList(builder, STUB_SUPPORT, RPAR, set); } } else { builder.error("'(' expected"); } if(target == Target.CONSTRUCTOR) { if(builder.getTokenType() == COLON) { builder.advanceLexer(); ExpressionParsing.parseConstructorSuperCall(builder, set); } } else if(target != Target.DECONSTRUCTOR) { GenericParameterParsing.parseGenericConstraintList(builder); } if(!expect(builder, SEMICOLON, null)) { if(builder.getTokenType() == LBRACE) { StatementParsing.parse(builder, set); } else if(builder.getTokenType() == DARROW) { builder.advanceLexer(); ExpressionParsing.parse(builder, set); expect(builder, SEMICOLON, "';' expected"); } else { builder.error("';' expected"); } } switch(target) { case DECONSTRUCTOR: case CONSTRUCTOR: done(marker, CONSTRUCTOR_DECLARATION); break; case METHOD: done(marker, METHOD_DECLARATION); break; case CONVERSION_METHOD: done(marker, CONVERSION_METHOD_DECLARATION); break; } } public static void parseParameterList(CSharpBuilderWrapper builder, int flags, IElementType end, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if(builder.getTokenType() != end) { while(!builder.eof()) { parseParameter(builder, end, flags, set); if(builder.getTokenType() == COMMA) { builder.advanceLexer(); } else if(builder.getTokenType() == end) { break; } else { PsiBuilder.Marker errorMarker = builder.mark(); builder.advanceLexer(); errorMarker.error("Expected comma"); } } } expect(builder, end, "')' expected"); mark.done(BitUtil.isSet(flags, STUB_SUPPORT) ? CSharpStubElements.PARAMETER_LIST : CSharpElements.PARAMETER_LIST); } private static void parseParameter(CSharpBuilderWrapper builder, IElementType end, int flags, ModifierSet set) { PsiBuilder.Marker mark = builder.mark(); Pair<PsiBuilder.Marker, ModifierSet> modifierPair = parseModifierListWithAttributes(builder, flags); TypeInfo typeInfo = parseType(builder, flags); if(typeInfo == null) { if(modifierPair.getSecond().isEmpty()) { // if no modifiers but we failed parse type - need go advance, due ill infinity loop inside parseParameterList, // but dont eat close brace if(builder.getTokenType() != end) { builder.advanceLexer(); } } mark.error("Type expected"); } else { if(typeInfo.nativeElementType != CSharpTokens.__ARGLIST_KEYWORD) { expectOrReportIdentifier(builder, flags); if(expect(builder, EQ, null)) { if(ExpressionParsing.parse(builder, set) == null) { builder.error("Expression expected."); } } } mark.done(BitUtil.isSet(flags, STUB_SUPPORT) ? CSharpStubElements.PARAMETER : CSharpElements.PARAMETER); } } }