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