#ref outside of declared guards #loc");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/InstanceGuardedByStaticInspection.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/InstanceGuardedByStaticInspection.java
new file mode 100644
index 000000000000..e2572a16e7e4
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/InstanceGuardedByStaticInspection.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.daemon.GroupNames;
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import com.intellij.psi.javadoc.PsiDocTag;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+public class InstanceGuardedByStaticInspection extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return GroupNames.CONCURRENCY_ANNOTATION_ISSUES;
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Instance member guarded by static field";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "InstanceGuardedByStatic";
+ }
+
+ @NotNull
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends JavaElementVisitor {
+ private final ProblemsHolder myHolder;
+
+ public Visitor(ProblemsHolder holder) {
+ myHolder = holder;
+ }
+
+
+ public void visitDocTag(PsiDocTag psiDocTag) {
+ super.visitDocTag(psiDocTag);
+ if (!JCiPUtil.isGuardedByTag(psiDocTag)) {
+ return;
+ }
+ final PsiMember member = PsiTreeUtil.getParentOfType(psiDocTag, PsiMember.class);
+ if (member == null) {
+ return;
+ }
+ if (member.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(psiDocTag);
+
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(psiDocTag, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (!guardField.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ myHolder.registerProblem(psiDocTag, "Instance member guarded by static \"" + guardValue + "\" #loc");
+ }
+
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ }
+
+ public void visitAnnotation(PsiAnnotation annotation) {
+ super.visitAnnotation(annotation);
+ if (!JCiPUtil.isGuardedByAnnotation(annotation)) {
+ return;
+ }
+ final PsiMember member = PsiTreeUtil.getParentOfType(annotation, PsiMember.class);
+ if (member == null) {
+ return;
+ }
+ if (member.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(annotation);
+ if (guardValue == null) {
+ return;
+ }
+
+ final PsiAnnotationMemberValue guardRef = annotation.findAttributeValue("value");
+ if (guardRef == null) {
+ return;
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(annotation, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (!guardField.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ myHolder.registerProblem(guardRef, "Instance member guarded by static #ref #loc");
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java
new file mode 100644
index 000000000000..af22103f32b6
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPOrderEntryFix.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2000-2009 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.
+ */
+
+/*
+ * User: anna
+ * Date: 30-Jul-2007
+ */
+package com.intellij.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.TargetElementUtil;
+import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.PathUtil;
+import net.jcip.annotations.GuardedBy;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public class JCiPOrderEntryFix implements IntentionAction {
+ private static final Logger LOG = Logger.getInstance("#" + JCiPOrderEntryFix.class.getName());
+
+ @NotNull
+ public String getText() {
+ return "Add jcip-annotations.jar to classpath";
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return getText();
+ }
+
+ public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) {
+ if (!(file instanceof PsiJavaFile)) return false;
+
+ final PsiReference reference = TargetElementUtil.findReference(editor);
+ if (!(reference instanceof PsiJavaCodeReferenceElement)) return false;
+ if (reference.resolve() != null) return false;
+ @NonNls final String referenceName = ((PsiJavaCodeReferenceElement)reference).getReferenceName();
+ if (referenceName == null) return false;
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ final VirtualFile virtualFile = file.getVirtualFile();
+ if (virtualFile == null) return false;
+ if (fileIndex.getModuleForFile(virtualFile) == null) return false;
+ if (!(((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnnotation &&
+ PsiUtil.isLanguageLevel5OrHigher(((PsiJavaCodeReferenceElement)reference)))) return false;
+ if (!JCiPUtil.isJCiPAnnotation(referenceName)) return false;
+ return true;
+ }
+
+ public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
+ final PsiJavaCodeReferenceElement reference = (PsiJavaCodeReferenceElement)TargetElementUtil.findReference(editor);
+ LOG.assertTrue(reference != null);
+ String jarPath = PathUtil.getJarPathForClass(GuardedBy.class);
+ final VirtualFile virtualFile = file.getVirtualFile();
+ LOG.assertTrue(virtualFile != null);
+ OrderEntryFix.addBundledJarToRoots(project, editor, ModuleUtil.findModuleForFile(virtualFile, project), reference,
+ "net.jcip.annotations." + reference.getReferenceName(), jarPath);
+ }
+
+ public boolean startInWriteAction() {
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPUtil.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPUtil.java
new file mode 100644
index 000000000000..755e568d170d
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/JCiPUtil.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.psi.*;
+import com.intellij.psi.javadoc.PsiDocTag;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class JCiPUtil {
+ @NonNls
+ private static final String IMMUTABLE = "net.jcip.annotations.Immutable";
+ @NonNls
+ private static final String GUARDED_BY = "net.jcip.annotations.GuardedBy";
+ @NonNls
+ private static final String THREAD_SAFE = "net.jcip.annotations.ThreadSafe";
+
+ public static boolean isJCiPAnnotation(String ref) {
+ return "Immutable".equals(ref) || "GuardedBy".equals(ref) || "ThreadSafe".equals("ref");
+ }
+
+ private JCiPUtil() {
+ }
+
+ public static boolean isImmutable(PsiClass aClass) {
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotation(aClass, IMMUTABLE);
+ if (annotation != null) {
+ return true;
+ }
+ final ImmutableTagVisitor visitor = new ImmutableTagVisitor();
+ aClass.accept(visitor);
+ return visitor.isFound();
+ }
+
+ @Nullable
+ public static String findGuardForMember(PsiMember member) {
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotation(member, GUARDED_BY);
+ if (annotation != null) {
+ return getGuardValue(annotation);
+ }
+
+ final GuardedTagVisitor visitor = new GuardedTagVisitor();
+ member.accept(visitor);
+ return visitor.getGuardString();
+ }
+
+ public static boolean isGuardedBy(PsiMember member, String guard) {
+
+ final PsiAnnotation annotation = AnnotationUtil.findAnnotation(member, GUARDED_BY);
+ if (annotation != null) {
+ final PsiAnnotationParameterList parameters = annotation.getParameterList();
+ final PsiNameValuePair[] pairs = parameters.getAttributes();
+ final String fieldName = '"' + guard + '"';
+ for (PsiNameValuePair pair : pairs) {
+ final String name = pair.getName();
+ if (("value".equals(name) || name == null)) {
+ final PsiAnnotationMemberValue value = pair.getValue();
+ if (value != null && value.getText().equals(fieldName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isGuardedBy(PsiMember member, PsiField field) {
+ return isGuardedBy(member, field.getName());
+ }
+
+ public static boolean isGuardedByAnnotation(PsiAnnotation annotation) {
+ return GUARDED_BY.equals(annotation.getQualifiedName());
+ }
+
+ public static boolean isGuardedByTag(PsiDocTag tag) {
+ final String text = tag.getText();
+
+ return text.startsWith("@GuardedBy") && text.contains("(") && text.contains(")");
+ }
+
+ @Nullable
+ public static String getGuardValue(PsiAnnotation annotation) {
+ final PsiAnnotationParameterList parameters = annotation.getParameterList();
+ final PsiNameValuePair[] pairs = parameters.getAttributes();
+ for (PsiNameValuePair pair : pairs) {
+ final String name = pair.getName();
+ if ("value".equals(name) || name == null) {
+ final PsiAnnotationMemberValue psiAnnotationMemberValue = pair.getValue();
+ if (psiAnnotationMemberValue != null) {
+ final String value = psiAnnotationMemberValue.getText();
+ return value.substring(1, value.length() - 1).trim();
+ }
+ }
+ }
+ return null;
+ }
+
+ @NotNull
+ public static String getGuardValue(PsiDocTag tag) {
+ final String text = tag.getText();
+ return text.substring(text.indexOf((int)'(') + 1, text.indexOf((int)')')).trim();
+ }
+
+ private static class GuardedTagVisitor extends JavaRecursiveElementVisitor {
+ private String guardString = null;
+
+ public void visitDocTag(PsiDocTag tag) {
+ super.visitDocTag(tag);
+ final String text = tag.getText();
+ if (text.startsWith("@GuardedBy") && text.contains("(") && text.contains(")")) {
+ guardString = text.substring(text.indexOf((int)'(') + 1, text.indexOf((int)')'));
+ }
+ }
+
+ @Nullable
+ public String getGuardString() {
+ return guardString;
+ }
+ }
+
+ private static class ImmutableTagVisitor extends JavaRecursiveElementVisitor {
+ private boolean found = false;
+
+ public void visitDocTag(PsiDocTag tag) {
+ super.visitDocTag(tag);
+ final String text = tag.getText();
+ if (text.startsWith("@Immutable")) {
+ found = true;
+ }
+ }
+
+ public boolean isFound() {
+ return found;
+ }
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalFieldInImmutableInspection.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalFieldInImmutableInspection.java
new file mode 100644
index 000000000000..0757fc0e9f6e
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalFieldInImmutableInspection.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.daemon.GroupNames;
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+public class NonFinalFieldInImmutableInspection extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return GroupNames.CONCURRENCY_ANNOTATION_ISSUES;
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Non-final field in @Immutable class";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "NonFinalFieldInImmutable";
+ }
+
+
+ @NotNull
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+ public void visitField(PsiField field) {
+ super.visitField(field);
+ if (field.hasModifierProperty(PsiModifier.FINAL)) {
+ return;
+ }
+ final PsiClass containingClass = field.getContainingClass();
+ if (!JCiPUtil.isImmutable(containingClass)) {
+ return;
+ }
+ holder.registerProblem(field, "Non-final field #ref in @Immutable class #loc");
+ }
+
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalGuardInspection.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalGuardInspection.java
new file mode 100644
index 000000000000..44a665860608
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/NonFinalGuardInspection.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.daemon.GroupNames;
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import com.intellij.psi.javadoc.PsiDocTag;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+public class NonFinalGuardInspection extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return GroupNames.CONCURRENCY_ANNOTATION_ISSUES;
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Non-final @GuardedBy field";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "NonFinalGuard";
+ }
+
+
+ @NotNull
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends JavaElementVisitor {
+ private final ProblemsHolder myHolder;
+
+ public Visitor(ProblemsHolder holder) {
+
+ myHolder = holder;
+ }
+
+ public void visitAnnotation(PsiAnnotation annotation) {
+ super.visitAnnotation(annotation);
+ if (!JCiPUtil.isGuardedByAnnotation(annotation)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(annotation);
+ if (guardValue == null || "this".equals(guardValue)) {
+ return;
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(annotation, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (guardField.hasModifierProperty(PsiModifier.FINAL)) {
+ return;
+ }
+ final PsiAnnotationMemberValue member = annotation.findAttributeValue("value");
+ if (member == null) {
+ return;
+ }
+ myHolder.registerProblem(member, "Non-final @GuardedBy field #ref #loc");
+ }
+
+ public void visitDocTag(PsiDocTag psiDocTag) {
+ super.visitDocTag(psiDocTag);
+ if (!JCiPUtil.isGuardedByTag(psiDocTag)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(psiDocTag);
+ if ("this".equals(guardValue)) {
+ return;
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(psiDocTag, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (guardField.hasModifierProperty(PsiModifier.FINAL)) {
+ return;
+ }
+ myHolder.registerProblem(psiDocTag, "Non-final @GuardedBy field \"" + guardValue + "\" #loc");
+ }
+
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/StaticGuardedByInstanceInspection.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/StaticGuardedByInstanceInspection.java
new file mode 100644
index 000000000000..41623606c6ee
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/StaticGuardedByInstanceInspection.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.daemon.GroupNames;
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import com.intellij.psi.javadoc.PsiDocTag;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+public class StaticGuardedByInstanceInspection extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return GroupNames.CONCURRENCY_ANNOTATION_ISSUES;
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Static member guarded by instance field or this";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "StaticGuardedByInstance";
+ }
+
+ @NotNull
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends JavaElementVisitor {
+ private final ProblemsHolder myHolder;
+
+ public Visitor(ProblemsHolder holder) {
+ myHolder = holder;
+ }
+
+ public void visitAnnotation(PsiAnnotation annotation) {
+ super.visitAnnotation(annotation);
+ if (!JCiPUtil.isGuardedByAnnotation(annotation)) {
+ return;
+ }
+ final PsiMember member = PsiTreeUtil.getParentOfType(annotation, PsiMember.class);
+ if (member == null) {
+ return;
+ }
+ if (!member.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(annotation);
+ if (guardValue == null) {
+ return;
+ }
+
+ final PsiAnnotationMemberValue guardRef = annotation.findAttributeValue("value");
+ if (guardRef == null) {
+ return;
+ }
+ if ("this".equals(guardValue)) {
+ registerError(guardRef);
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(annotation, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (guardField.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ registerError(guardRef);
+ }
+
+ public void visitDocTag(PsiDocTag psiDocTag) {
+ super.visitDocTag(psiDocTag);
+ if (!JCiPUtil.isGuardedByTag(psiDocTag)) {
+ return;
+ }
+ final PsiMember member = PsiTreeUtil.getParentOfType(psiDocTag, PsiMember.class);
+ if (member == null) {
+ return;
+ }
+ if (!member.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(psiDocTag);
+
+ if ("this".equals(guardValue)) {
+ registerError(psiDocTag);
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(psiDocTag, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField == null) {
+ return;
+ }
+ if (guardField.hasModifierProperty(PsiModifier.STATIC)) {
+ return;
+ }
+ myHolder.registerProblem(psiDocTag, "Static member guarded by instance \"" + guardValue + "\" #loc");
+ }
+
+ private void registerError(PsiElement element) {
+ myHolder.registerProblem(element, "Static member guarded by instance #ref #loc");
+ }
+
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/UnknownGuardInspection.java b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/UnknownGuardInspection.java
new file mode 100644
index 000000000000..8940bd6c98e0
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/concurrencyAnnotations/UnknownGuardInspection.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2009 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.codeInspection.concurrencyAnnotations;
+
+import com.intellij.codeInsight.daemon.GroupNames;
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.*;
+import com.intellij.psi.javadoc.PsiDocTag;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+public class UnknownGuardInspection extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return GroupNames.CONCURRENCY_ANNOTATION_ISSUES;
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Unknown @GuardedBy field";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "UnknownGuard";
+ }
+
+ @NotNull
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends JavaElementVisitor {
+ private final ProblemsHolder myHolder;
+
+ public Visitor(ProblemsHolder holder) {
+ myHolder = holder;
+ }
+
+ public void visitAnnotation(PsiAnnotation annotation) {
+ super.visitAnnotation(annotation);
+ if (!JCiPUtil.isGuardedByAnnotation(annotation)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(annotation);
+ if (guardValue == null || "this".equals(guardValue)) {
+ return;
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(annotation, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField != null) {
+ return;
+ }
+ final PsiAnnotationMemberValue member = annotation.findAttributeValue("value");
+ if (member == null) {
+ return;
+ }
+ myHolder.registerProblem(member, "Unknown @GuardedBy field #ref #loc");
+ }
+
+ public void visitDocTag(PsiDocTag psiDocTag) {
+ super.visitDocTag(psiDocTag);
+ if (!JCiPUtil.isGuardedByTag(psiDocTag)) {
+ return;
+ }
+ final String guardValue = JCiPUtil.getGuardValue(psiDocTag);
+ if ("this".equals(guardValue)) {
+ return;
+ }
+ final PsiClass containingClass = PsiTreeUtil.getParentOfType(psiDocTag, PsiClass.class);
+ if (containingClass == null) {
+ return;
+ }
+ final PsiField guardField = containingClass.findFieldByName(guardValue, true);
+ if (guardField != null) {
+ return;
+ }
+ myHolder.registerProblem(psiDocTag, "Unknown @GuardedBy field \"" + guardValue + "\" #loc");
+ }
+
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ }
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java b/java/java-impl/src/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java
index 033ff9d0d69e..5ced11509a14 100644
--- a/java/java-impl/src/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java
+++ b/java/java-impl/src/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.LossyEncodingInspection;
import com.intellij.codeInspection.RedundantSuppressInspection;
import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance;
import com.intellij.codeInspection.canBeFinal.CanBeFinalInspection;
+import com.intellij.codeInspection.concurrencyAnnotations.*;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.codeInspection.deadCode.DeadCodeInspection;
import com.intellij.codeInspection.defUse.DefUseInspection;
@@ -108,7 +109,14 @@ public class StandardInspectionToolsProvider implements InspectionToolProvider {
UncheckedWarningLocalInspection.class,
SuspiciousNameCombinationInspection.class,
DuplicateThrowsInspection.class,
- LossyEncodingInspection.class
+ LossyEncodingInspection.class,
+
+ FieldAccessNotGuardedInspection.class,
+ InstanceGuardedByStaticInspection.class,
+ NonFinalFieldInImmutableInspection.class,
+ NonFinalGuardInspection.class,
+ StaticGuardedByInstanceInspection.class,
+ UnknownGuardInspection.class
};
}
}
diff --git a/java/openapi/src/com/intellij/codeInsight/daemon/GroupNames.java b/java/openapi/src/com/intellij/codeInsight/daemon/GroupNames.java
index c9571cf0d8f0..48c21641e9aa 100644
--- a/java/openapi/src/com/intellij/codeInsight/daemon/GroupNames.java
+++ b/java/openapi/src/com/intellij/codeInsight/daemon/GroupNames.java
@@ -66,4 +66,5 @@ public interface GroupNames {
String MODULARIZATION_GROUP_NAME = InspectionsBundle.message("group.names.modularization.issues");
String JAVAEE_GROUP_NAME = InspectionsBundle.message("group.names.javaee.issues");
+ String CONCURRENCY_ANNOTATION_ISSUES = "Concurrency annotation issues";
}
diff --git a/lib/jcip-annotations.jar b/lib/jcip-annotations.jar
new file mode 100644
index 000000000000..06e9066b880d
Binary files /dev/null and b/lib/jcip-annotations.jar differ
diff --git a/resources-en/src/inspectionDescriptions/FieldAccessNotGuarded.html b/resources-en/src/inspectionDescriptions/FieldAccessNotGuarded.html
new file mode 100644
index 000000000000..3d7ac75b45a6
--- /dev/null
+++ b/resources-en/src/inspectionDescriptions/FieldAccessNotGuarded.html
@@ -0,0 +1,16 @@
+
+
+| + + This inspection reports any accesses of fields declared as @net.jcip.annotations.GuardedBy + which are are not guarded by an appropriate synchronization structure. + | +
| New in 9 | +
| + + This inspection reports any @net.jcip.annotations.GuardedBy annotations on instance fields or methods, + where + the guard is a static field. Guarding a non-static by a static may result in excessive lock contention, + as access to each locked field in any object instance will prevent simultaneous access to that field in + every object + instance. + | +
| New in 9 | +
| + + This inspection reports any non-final field in a class with annotation + @net.jcip.annotations.Immutable. This violates the contract of the @Immutable + annotation. + | +
| New in 9 | +
| + + This inspection reports any @net.jcip.annotations.GuardedBy annotations where the guarding field + is not final. Gaurding on a non-final field may result in unexpected race conditions, as locks will + be held on the value of the field (which may change), rather than the field itself. + | +
| New in 9 | +
| + + This inspection reports any @net.jcip.annotations.GuardedBy annotations on static fields or methods, + where + the guard is either a non-static field or 'this'. Guarding a static by a non-static may result in + excessive + concurrency, multiple threads may be able to access the guarded field simultaneously, by locking in + different + object contexts. + | +
| New in 9 | +
| + + This inspection reports any @net.jcip.annotations.GuardedBy annotations where the guarding field + is unknown. + | +
| New in 9 | +