blob: 540f52ca89e91ab2c48569e3ba275d813e4366b2 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.appengine.inspections;
import com.intellij.appengine.facet.AppEngineFacet;
import com.intellij.appengine.sdk.AppEngineSdk;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.*;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JdkOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.ClassUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* @author nik
*/
public class AppEngineForbiddenCodeInspection extends BaseJavaLocalInspectionTool {
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
final Project project = manager.getProject();
Module module = ModuleUtilCore.findModuleForPsiElement(file);
final AppEngineFacet appEngineFacet = AppEngineFacet.getAppEngineFacetByModule(module);
if (appEngineFacet == null) {
return null;
}
final AppEngineSdk appEngineSdk = appEngineFacet.getSdk();
if (!appEngineSdk.isValid()) {
return null;
}
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final List<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>();
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitDocComment(PsiDocComment comment) {
}
@Override
public void visitMethod(PsiMethod method) {
final PsiModifierList modifierList = method.getModifierList();
if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) {
if (!isNativeMethodAllowed(method)) {
problems.add(manager.createProblemDescriptor(modifierList, "Native methods aren't allowed in App Engine application", isOnTheFly,
LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
super.visitMethod(method);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
if (classReference != null) {
final PsiElement resolved = classReference.resolve();
if (resolved instanceof PsiClass) {
final String qualifiedName = ((PsiClass)resolved).getQualifiedName();
if (qualifiedName != null && appEngineSdk.isMethodInBlacklist(qualifiedName, "new")) {
final String message = "App Engine application should not create new instances of '" + qualifiedName + "' class";
problems.add(manager.createProblemDescriptor(classReference, message, isOnTheFly, LocalQuickFix.EMPTY_ARRAY,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
}
super.visitNewExpression(expression);
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
final PsiElement element = methodExpression.resolve();
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
final PsiClass psiClass = method.getContainingClass();
if (psiClass != null) {
final String qualifiedName = psiClass.getQualifiedName();
final String methodName = method.getName();
if (qualifiedName != null && appEngineSdk.isMethodInBlacklist(qualifiedName, methodName)) {
final String message =
"AppEngine application should not call '" + StringUtil.getShortName(qualifiedName) + "." + methodName + "' method";
problems.add(manager.createProblemDescriptor(methodExpression, message, isOnTheFly, LocalQuickFix.EMPTY_ARRAY,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
}
super.visitMethodCallExpression(expression);
}
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiClass) {
final PsiFile psiFile = resolved.getContainingFile();
if (psiFile != null) {
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null && !fileIndex.isInSource(virtualFile)) {
final List<OrderEntry> list = fileIndex.getOrderEntriesForFile(virtualFile);
for (OrderEntry entry : list) {
if (entry instanceof JdkOrderEntry) {
final String className = ClassUtil.getJVMClassName((PsiClass)resolved);
if (className != null && !appEngineSdk.isClassInWhiteList(className)) {
problems.add(manager.createProblemDescriptor(reference, "Class '" + className + "' is not included in App Engine JRE White List",
isOnTheFly, LocalQuickFix.EMPTY_ARRAY,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
}
}
}
}
}
}
super.visitReferenceElement(reference);
}
});
return problems.toArray(new ProblemDescriptor[problems.size()]);
}
private static boolean isNativeMethodAllowed(PsiMethod method) {
for (AppEngineForbiddenCodeHandler handler : AppEngineForbiddenCodeHandler.EP_NAME.getExtensions()) {
if (handler.isNativeMethodAllowed(method)) {
return true;
}
}
return false;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@NotNull
@Override
public HighlightDisplayLevel getDefaultLevel() {
return HighlightDisplayLevel.ERROR;
}
@Nls
@NotNull
public String getGroupDisplayName() {
return "Google App Engine";
}
@Nls
@NotNull
public String getDisplayName() {
return "Forbidden code in App Engine applications";
}
@NotNull
public String getShortName() {
return "AppEngineForbiddenCode";
}
}