001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.idea; 018 019 import com.intellij.util.NotNullFunction; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.annotations.Nullable; 022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 023 import org.jetbrains.kotlin.descriptors.*; 024 import org.jetbrains.kotlin.psi.KtAnnotationEntry; 025 import org.jetbrains.kotlin.psi.KtDeclaration; 026 import org.jetbrains.kotlin.psi.KtFile; 027 import org.jetbrains.kotlin.psi.KtNamedFunction; 028 import org.jetbrains.kotlin.resolve.BindingContext; 029 import org.jetbrains.kotlin.resolve.DescriptorUtils; 030 import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt; 031 import org.jetbrains.kotlin.types.KotlinType; 032 import org.jetbrains.kotlin.types.TypeProjection; 033 import org.jetbrains.kotlin.types.Variance; 034 035 import java.util.Collection; 036 import java.util.List; 037 038 public class MainFunctionDetector { 039 private final NotNullFunction<KtNamedFunction, FunctionDescriptor> getFunctionDescriptor; 040 041 /** Assumes that the function declaration is already resolved and the descriptor can be found in the {@code bindingContext}. */ 042 public MainFunctionDetector(@NotNull final BindingContext bindingContext) { 043 this.getFunctionDescriptor = new NotNullFunction<KtNamedFunction, FunctionDescriptor>() { 044 @NotNull 045 @Override 046 public FunctionDescriptor fun(KtNamedFunction function) { 047 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function); 048 if (functionDescriptor == null) { 049 throw new IllegalStateException("No descriptor resolved for " + function + " " + function.getText()); 050 } 051 return functionDescriptor; 052 } 053 }; 054 } 055 056 public MainFunctionDetector(@NotNull NotNullFunction<KtNamedFunction, FunctionDescriptor> functionResolver) { 057 this.getFunctionDescriptor = functionResolver; 058 } 059 060 public boolean hasMain(@NotNull List<KtDeclaration> declarations) { 061 return findMainFunction(declarations) != null; 062 } 063 064 public boolean isMain(@NotNull KtNamedFunction function) { 065 if (function.isLocal()) { 066 return false; 067 } 068 069 if (function.getValueParameters().size() != 1 || !function.getTypeParameters().isEmpty()) { 070 return false; 071 } 072 073 /* Psi only check for kotlin.jvm.jvmName annotation */ 074 if (!"main".equals(function.getName()) && !hasAnnotationWithExactNumberOfArguments(function, 1)) { 075 return false; 076 } 077 078 /* Psi only check for kotlin.jvm.jvmStatic annotation */ 079 if (!function.isTopLevel() && !hasAnnotationWithExactNumberOfArguments(function, 0)) { 080 return false; 081 } 082 083 return isMain(getFunctionDescriptor.fun(function)); 084 } 085 086 public static boolean isMain(@NotNull DeclarationDescriptor descriptor) { 087 if (!(descriptor instanceof FunctionDescriptor)) return false; 088 089 FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; 090 if (!getJVMFunctionName(functionDescriptor).equals("main")) { 091 return false; 092 } 093 094 List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters(); 095 if (parameters.size() != 1 || !functionDescriptor.getTypeParameters().isEmpty()) return false; 096 097 ValueParameterDescriptor parameter = parameters.get(0); 098 KotlinType parameterType = parameter.getType(); 099 if (!KotlinBuiltIns.isArray(parameterType)) return false; 100 101 List<TypeProjection> typeArguments = parameterType.getArguments(); 102 if (typeArguments.size() != 1) return false; 103 104 KotlinType typeArgument = typeArguments.get(0).getType(); 105 if (!KotlinBuiltIns.isString(typeArgument)) { 106 return false; 107 } 108 if (typeArguments.get(0).getProjectionKind() == Variance.IN_VARIANCE) { 109 return false; 110 } 111 112 if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return true; 113 114 DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); 115 return containingDeclaration instanceof ClassDescriptor 116 && ((ClassDescriptor) containingDeclaration).getKind().isSingleton() 117 && AnnotationUtilKt.hasJvmStaticAnnotation(functionDescriptor); 118 } 119 120 @Nullable 121 public KtNamedFunction getMainFunction(@NotNull Collection<KtFile> files) { 122 for (KtFile file : files) { 123 KtNamedFunction mainFunction = findMainFunction(file.getDeclarations()); 124 if (mainFunction != null) { 125 return mainFunction; 126 } 127 } 128 return null; 129 } 130 131 @Nullable 132 private KtNamedFunction findMainFunction(@NotNull List<KtDeclaration> declarations) { 133 for (KtDeclaration declaration : declarations) { 134 if (declaration instanceof KtNamedFunction) { 135 KtNamedFunction candidateFunction = (KtNamedFunction) declaration; 136 if (isMain(candidateFunction)) { 137 return candidateFunction; 138 } 139 } 140 } 141 return null; 142 } 143 144 @NotNull 145 private static String getJVMFunctionName(FunctionDescriptor functionDescriptor) { 146 String platformName = DescriptorUtils.getJvmName(functionDescriptor); 147 if (platformName != null) { 148 return platformName; 149 } 150 151 return functionDescriptor.getName().asString(); 152 } 153 154 private static boolean hasAnnotationWithExactNumberOfArguments(@NotNull KtNamedFunction function, int number) { 155 for (KtAnnotationEntry entry : function.getAnnotationEntries()) { 156 if (entry.getValueArguments().size() == number) { 157 return true; 158 } 159 } 160 161 return false; 162 } 163 }