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