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.JetDeclaration; 025 import org.jetbrains.kotlin.psi.JetFile; 026 import org.jetbrains.kotlin.psi.JetNamedFunction; 027 import org.jetbrains.kotlin.resolve.BindingContext; 028 import org.jetbrains.kotlin.resolve.DescriptorUtils; 029 import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage; 030 import org.jetbrains.kotlin.types.JetType; 031 import org.jetbrains.kotlin.types.TypeProjection; 032 import org.jetbrains.kotlin.types.checker.JetTypeChecker; 033 034 import java.util.Collection; 035 import java.util.List; 036 037 public class MainFunctionDetector { 038 private final NotNullFunction<JetNamedFunction, FunctionDescriptor> getFunctionDescriptor; 039 040 /** Assumes that the function declaration is already resolved and the descriptor can be found in the {@code bindingContext}. */ 041 public MainFunctionDetector(@NotNull final BindingContext bindingContext) { 042 this.getFunctionDescriptor = new NotNullFunction<JetNamedFunction, FunctionDescriptor>() { 043 @NotNull 044 @Override 045 public FunctionDescriptor fun(JetNamedFunction function) { 046 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function); 047 if (functionDescriptor == null) { 048 throw new IllegalStateException("No descriptor resolved for " + function + " " + function.getText()); 049 } 050 return functionDescriptor; 051 } 052 }; 053 } 054 055 public MainFunctionDetector(@NotNull NotNullFunction<JetNamedFunction, FunctionDescriptor> functionResolver) { 056 this.getFunctionDescriptor = functionResolver; 057 } 058 059 public boolean hasMain(@NotNull List<JetDeclaration> declarations) { 060 return findMainFunction(declarations) != null; 061 } 062 063 public boolean isMain(@NotNull JetNamedFunction function) { 064 if (!"main".equals(function.getName())) return false; 065 066 FunctionDescriptor functionDescriptor = getFunctionDescriptor.fun(function); 067 List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters(); 068 if (parameters.size() != 1) return false; 069 070 ValueParameterDescriptor parameter = parameters.get(0); 071 JetType parameterType = parameter.getType(); 072 KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance(); 073 if (!KotlinBuiltIns.isArray(parameterType)) return false; 074 075 List<TypeProjection> typeArguments = parameterType.getArguments(); 076 if (typeArguments.size() != 1) return false; 077 078 JetType typeArgument = typeArguments.get(0).getType(); 079 if (!JetTypeChecker.DEFAULT.equalTypes(typeArgument, kotlinBuiltIns.getStringType())) return false; 080 081 if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return true; 082 083 DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); 084 return containingDeclaration instanceof ClassDescriptor 085 && ((ClassDescriptor) containingDeclaration).getKind().isSingleton() 086 && AnnotationsPackage.hasPlatformStaticAnnotation(functionDescriptor); 087 } 088 089 @Nullable 090 public JetNamedFunction getMainFunction(@NotNull Collection<JetFile> files) { 091 for (JetFile file : files) { 092 JetNamedFunction mainFunction = findMainFunction(file.getDeclarations()); 093 if (mainFunction != null) { 094 return mainFunction; 095 } 096 } 097 return null; 098 } 099 100 @Nullable 101 private JetNamedFunction findMainFunction(@NotNull List<JetDeclaration> declarations) { 102 for (JetDeclaration declaration : declarations) { 103 if (declaration instanceof JetNamedFunction) { 104 JetNamedFunction candidateFunction = (JetNamedFunction) declaration; 105 if (isMain(candidateFunction)) { 106 return candidateFunction; 107 } 108 } 109 } 110 return null; 111 } 112 }