001 /* 002 * Copyright 2010-2014 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.jet.lang.resolve; 018 019 import com.google.common.collect.Lists; 020 import com.google.common.collect.Maps; 021 import com.google.common.collect.Sets; 022 import com.intellij.lang.ASTNode; 023 import com.intellij.openapi.util.Pair; 024 import com.intellij.psi.PsiElement; 025 import org.jetbrains.annotations.NotNull; 026 import org.jetbrains.annotations.Nullable; 027 import org.jetbrains.jet.lang.descriptors.*; 028 import org.jetbrains.jet.lang.diagnostics.Errors; 029 import org.jetbrains.jet.lang.psi.JetClass; 030 import org.jetbrains.jet.lang.psi.JetEnumEntry; 031 import org.jetbrains.jet.lang.psi.JetModifierList; 032 import org.jetbrains.jet.lang.psi.JetModifierListOwner; 033 import org.jetbrains.jet.lexer.JetModifierKeywordToken; 034 035 import java.util.Collection; 036 import java.util.Collections; 037 import java.util.Map; 038 039 import static org.jetbrains.jet.lang.diagnostics.Errors.ILLEGAL_MODIFIER; 040 import static org.jetbrains.jet.lexer.JetTokens.*; 041 042 public class ModifiersChecker { 043 private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS = 044 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD); 045 046 private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS = 047 Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD); 048 049 public static void reportIllegalModifiers( 050 @Nullable JetModifierList modifierList, 051 @NotNull Collection<JetModifierKeywordToken> illegalModifiers, 052 @NotNull BindingTrace trace 053 ) { 054 if (modifierList == null) return; 055 056 for (JetModifierKeywordToken modifierToken : illegalModifiers) { 057 if (modifierList.hasModifier(modifierToken)) { 058 PsiElement modifierPsi = modifierList.getModifier(modifierToken); 059 assert modifierPsi != null; 060 trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken)); 061 } 062 } 063 } 064 065 @NotNull 066 private final BindingTrace trace; 067 068 public ModifiersChecker(@NotNull BindingTrace trace) { 069 this.trace = trace; 070 } 071 072 public static ModifiersChecker create(@NotNull BindingTrace trace) { 073 return new ModifiersChecker(trace); 074 } 075 076 public void checkModifiersForDeclaration(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 077 JetModifierList modifierList = modifierListOwner.getModifierList(); 078 checkModalityModifiers(modifierList); 079 checkVisibilityModifiers(modifierList, descriptor); 080 checkInnerModifier(modifierListOwner, descriptor); 081 } 082 083 public void checkModifiersForLocalDeclaration(@NotNull JetModifierListOwner modifierListOwner) { 084 checkIllegalModalityModifiers(modifierListOwner); 085 checkIllegalVisibilityModifiers(modifierListOwner); 086 } 087 088 public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 089 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), MODALITY_MODIFIERS); 090 } 091 092 public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 093 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), VISIBILITY_MODIFIERS); 094 } 095 096 private void checkModalityModifiers(@Nullable JetModifierList modifierList) { 097 if (modifierList == null) return; 098 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD)); 099 100 checkCompatibility(modifierList, Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD), 101 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD)); 102 } 103 104 private void checkVisibilityModifiers(@Nullable JetModifierList modifierList, @NotNull DeclarationDescriptor descriptor) { 105 if (modifierList == null) return; 106 107 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 108 if (containingDeclaration instanceof PackageFragmentDescriptor) { 109 if (modifierList.hasModifier(PROTECTED_KEYWORD)) { 110 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierList.getModifierNode(PROTECTED_KEYWORD).getPsi())); 111 } 112 } 113 114 checkCompatibility(modifierList, VISIBILITY_MODIFIERS); 115 } 116 117 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 118 JetModifierList modifierList = modifierListOwner.getModifierList(); 119 120 if (modifierList != null && modifierList.hasModifier(INNER_KEYWORD)) { 121 if (isIllegalInner(descriptor)) { 122 checkIllegalInThisContextModifiers(modifierList, Collections.singletonList(INNER_KEYWORD)); 123 } 124 } 125 else { 126 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry) && isIllegalNestedClass(descriptor)) { 127 PsiElement name = ((JetClass) modifierListOwner).getNameIdentifier(); 128 if (name != null) { 129 trace.report(Errors.NESTED_CLASS_NOT_ALLOWED.on(name)); 130 } 131 } 132 } 133 } 134 135 private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) { 136 if (!(descriptor instanceof ClassDescriptor)) return true; 137 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; 138 if (classDescriptor.getKind() != ClassKind.CLASS) return true; 139 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration(); 140 if (!(containingDeclaration instanceof ClassDescriptor)) return true; 141 return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT; 142 } 143 144 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) { 145 if (!(descriptor instanceof ClassDescriptor)) return false; 146 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 147 if (!(containingDeclaration instanceof ClassDescriptor)) return false; 148 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration; 149 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor; 150 } 151 152 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) { 153 if (modifierList == null) return; 154 Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet(); 155 for (JetModifierKeywordToken modifier : availableModifiers) { 156 if (modifierList.hasModifier(modifier)) { 157 presentModifiers.add(modifier); 158 } 159 } 160 if (presentModifiers.size() == 1) { 161 return; 162 } 163 for (Collection<JetModifierKeywordToken> combination : availableCombinations) { 164 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) { 165 return; 166 } 167 } 168 for (JetModifierKeywordToken token : presentModifiers) { 169 trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers)); 170 } 171 } 172 173 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) { 174 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) { 175 JetModifierKeywordToken redundantModifier = tokenPair.getFirst(); 176 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond(); 177 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) { 178 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier)); 179 } 180 } 181 } 182 183 public void checkIllegalInThisContextModifiers( 184 @Nullable JetModifierList modifierList, 185 @NotNull Collection<JetModifierKeywordToken> illegalModifiers 186 ) { 187 reportIllegalModifiers(modifierList, illegalModifiers, trace); 188 } 189 190 @NotNull 191 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) { 192 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap(); 193 for (JetModifierKeywordToken modifier : possibleModifiers) { 194 if (modifierList.hasModifier(modifier)) { 195 nodes.put(modifier, modifierList.getModifierNode(modifier)); 196 } 197 } 198 return nodes; 199 } 200 201 @NotNull 202 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) { 203 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality); 204 } 205 206 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) { 207 if (modifierList == null) return defaultModality; 208 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD); 209 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD); 210 211 if (modifierList.hasModifier(OPEN_KEYWORD)) { 212 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) { 213 return Modality.ABSTRACT; 214 } 215 return Modality.OPEN; 216 } 217 if (hasAbstractModifier) { 218 return Modality.ABSTRACT; 219 } 220 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD); 221 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) { 222 return Modality.OPEN; 223 } 224 if (hasFinalModifier) { 225 return Modality.FINAL; 226 } 227 return defaultModality; 228 } 229 230 @NotNull 231 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) { 232 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility); 233 } 234 235 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) { 236 if (modifierList == null) return defaultVisibility; 237 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE; 238 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC; 239 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED; 240 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL; 241 return defaultVisibility; 242 } 243 244 public static boolean isInnerClass(@Nullable JetModifierList modifierList) { 245 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD); 246 } 247 248 @NotNull 249 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) { 250 ClassKind kind = descriptor.getKind(); 251 if (kind == ClassKind.ENUM_ENTRY) { 252 return Visibilities.PUBLIC; 253 } 254 if (kind == ClassKind.CLASS_OBJECT) { 255 return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility(); 256 } 257 return Visibilities.INTERNAL; 258 } 259 }