diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index 95fa1b1..5aafa8f 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2011 IBM Corporation and others. + * Copyright (c) 2006, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -397,6 +397,7 @@ expectedProblemAttributes.put("CodeCannotBeReached", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("CodeSnippetMissingClass", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("CodeSnippetMissingMethod", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); + expectedProblemAttributes.put("ContradictoryNullAnnotations", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("ComparingIdentical", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("ConflictingImport", new ProblemAttributes(CategorizedProblem.CAT_IMPORT)); expectedProblemAttributes.put("ConstructorVarargsArgumentNeedCast", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); @@ -790,6 +791,10 @@ expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotation", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationPackage", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationType", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationMethod", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); @@ -1089,6 +1094,7 @@ expectedProblemAttributes.put("CodeSnippetMissingMethod", SKIP); expectedProblemAttributes.put("ComparingIdentical", new ProblemAttributes(JavaCore.COMPILER_PB_COMPARING_IDENTICAL)); expectedProblemAttributes.put("ConflictingImport", SKIP); + expectedProblemAttributes.put("ContradictoryNullAnnotations", SKIP); expectedProblemAttributes.put("ConstructorVarargsArgumentNeedCast", new ProblemAttributes(JavaCore.COMPILER_PB_VARARGS_ARGUMENT_NEED_CAST)); expectedProblemAttributes.put("CorruptedSignature", SKIP); expectedProblemAttributes.put("DeadCode", new ProblemAttributes(JavaCore.COMPILER_PB_DEAD_CODE)); @@ -1480,6 +1486,10 @@ expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_TYPE_ARGUMENTS)); expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationPackage", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationType", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION)); + expectedProblemAttributes.put("RedundantNullDefaultAnnotationMethod", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java index 2e13dc6..df4bb59 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java @@ -53,7 +53,7 @@ // Static initializer to specify tests subset using TESTS_* static variables // All specified tests which do not belong to the class are skipped... static { -// TESTS_NAMES = new String[] { "test_default_nullness_014" }; +// TESTS_NAMES = new String[] { "test_redundant_annotation_" }; // TESTS_NUMBERS = new int[] { 561 }; // TESTS_RANGE = new int[] { 1, 2049 }; } @@ -2537,6 +2537,12 @@ " new C(null);\n" + " ^^^^\n" + "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + + "----------\n" + + "----------\n" + + "1. WARNING in p1\\C.java (at line 2)\n" + + " @org.eclipse.jdt.annotation.NonNullByDefault\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing package p1\n" + "----------\n"); } // Bug 365836 - [compiler][null] Incomplete propagation of null defaults. @@ -2654,6 +2660,253 @@ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + "----------\n"); } + +// redundant default annotations - class vs. inner class +public void test_redundant_annotation_01() { + Map customOptions = getCompilerOptions(); +// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); + runConformTestWithLibs( + new String[] { + "p2/Y.java", + "package p2;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "@NonNullByDefault\n" + + "public class Y {\n" + + " @NonNullByDefault class Inner {\n" + + " @NonNullByDefault class DeepInner {}\n" + + " }\n" + + " class Inner2 {\n" + + " @NonNullByDefault class DeepInner2 {\n" + + " }\n" + + " void foo() {\n" + + " @SuppressWarnings(\"unused\") @NonNullByDefault class Local {}\n" + + " }\n" + + " }\n" + + "}\n" + + "@NonNullByDefault class V {}\n", + "p3/package-info.java", + "@org.eclipse.jdt.annotation.NonNullByDefault package p3;\n", + "p3/Z.java", + "package p3;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "@NonNullByDefault\n" + + "public class Z {\n" + + "}\n" + + "class X {\n" + + " @NonNullByDefault class Inner {}\n" + + " class Inner2 {\n" + + " @NonNullByDefault class DeepInner {}\n" + + " }\n" + + "}\n" + }, + customOptions, + "----------\n" + + "1. WARNING in p2\\Y.java (at line 5)\n" + + " @NonNullByDefault class Inner {\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "2. WARNING in p2\\Y.java (at line 6)\n" + + " @NonNullByDefault class DeepInner {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y.Inner\n" + + "----------\n" + + "3. WARNING in p2\\Y.java (at line 9)\n" + + " @NonNullByDefault class DeepInner2 {\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "4. WARNING in p2\\Y.java (at line 12)\n" + + " @SuppressWarnings(\"unused\") @NonNullByDefault class Local {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "----------\n" + + "1. WARNING in p3\\Z.java (at line 3)\n" + + " @NonNullByDefault\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing package p3\n" + + "----------\n" + + "2. WARNING in p3\\Z.java (at line 7)\n" + + " @NonNullByDefault class Inner {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing package p3\n" + + "----------\n" + + "3. WARNING in p3\\Z.java (at line 9)\n" + + " @NonNullByDefault class DeepInner {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing package p3\n" + + "----------\n"); +} +// redundant default annotations - class vs. method +public void test_redundant_annotation_02() { + Map customOptions = getCompilerOptions(); + runConformTestWithLibs( + new String[] { + "p2/Y.java", + "package p2;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "@NonNullByDefault\n" + + "public class Y {\n" + + " @NonNullByDefault void foo() {}\n" + + "}\n" + + "class Z {\n" + + " @NonNullByDefault void bar() {\n" + + " @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" + + " @NonNullByDefault void fubar() {}\n" + + " }\n" + + " }\n" + + " @NonNullByDefault void zink() {\n" + + " @SuppressWarnings(\"unused\") class Bork {\n" + + " @NonNullByDefault void jubar() {}\n" + + " }\n" + + " }\n" + + "}\n" + }, + customOptions, + "----------\n" + + "1. WARNING in p2\\Y.java (at line 5)\n" + + " @NonNullByDefault void foo() {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "2. WARNING in p2\\Y.java (at line 9)\n" + + " @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing method bar()\n" + + "----------\n" + + "3. WARNING in p2\\Y.java (at line 10)\n" + + " @NonNullByDefault void fubar() {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Zork\n" + + "----------\n" + + "4. WARNING in p2\\Y.java (at line 15)\n" + + " @NonNullByDefault void jubar() {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing method zink()\n" + + "----------\n"); +} +//redundant default annotations - class vs. method - generics +public void test_redundant_annotation_02g() { + Map customOptions = getCompilerOptions(); + runConformTestWithLibs( + new String[] { + "p2/Y.java", + "package p2;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "@NonNullByDefault\n" + + "public class Y {\n" + + " @NonNullByDefault void foo(TF arg) {}\n" + + "}\n" + + "class Z {\n" + + " @NonNullByDefault void bar() {\n" + + " @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" + + " @NonNullByDefault void fubar(TB arg) {}\n" + + " }\n" + + " }\n" + + "}\n" + }, + customOptions, + "----------\n" + + "1. WARNING in p2\\Y.java (at line 5)\n" + + " @NonNullByDefault void foo(TF arg) {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "2. WARNING in p2\\Y.java (at line 9)\n" + + " @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing method bar()\n" + + "----------\n" + + "3. WARNING in p2\\Y.java (at line 10)\n" + + " @NonNullByDefault void fubar(TB arg) {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Zork\n" + + "----------\n"); +} + +// redundant default annotations - package / class / method vs global default +public void test_redundant_annotation_03() { + Map customOptions = getCompilerOptions(); + customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); + runConformTestWithLibs( + new String[] { + "p2/Y.java", + "package p2;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "@NonNullByDefault\n" + + "public class Y {\n" + + " @NonNullByDefault void foo() {}\n" + + "}\n" + + "class Z {\n" + + " @NonNullByDefault void bar() {}\n" + + "}\n", + "p3/package-info.java", + "@org.eclipse.jdt.annotation.NonNullByDefault package p3;\n" + }, + customOptions, + "----------\n" + + "1. WARNING in p2\\Y.java (at line 3)\n" + + " @NonNullByDefault\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with the global default\n" + + "----------\n" + + "2. WARNING in p2\\Y.java (at line 5)\n" + + " @NonNullByDefault void foo() {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with a default specified for the enclosing type Y\n" + + "----------\n" + + "3. WARNING in p2\\Y.java (at line 8)\n" + + " @NonNullByDefault void bar() {}\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with the global default\n" + + "----------\n" + + "----------\n" + + "1. WARNING in p3\\package-info.java (at line 1)\n" + + " @org.eclipse.jdt.annotation.NonNullByDefault package p3;\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Nullness default is redundant with the global default\n" + + "----------\n"); +} + +// contradictory null annotations +public void test_contradictory_annotations_01() { + Map customOptions = getCompilerOptions(); + runNegativeTestWithLibs( + new String[] { + "p2/Y.java", + "package p2;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "public class Y {\n" + + " void foo(@NonNull @Nullable Object o) {}\n" + + " @Nullable @NonNull Object bar() {\n" + + " @NonNull @Nullable Object o = null;\n" + + " return o;\n" + + " }\n" + + "}\n" + + "class Z {\n" + + " @NonNullByDefault void bar() {}\n" + + "}\n" + }, + customOptions, + "----------\n" + + "1. ERROR in p2\\Y.java (at line 4)\n" + + " void foo(@NonNull @Nullable Object o) {}\n" + + " ^^^^^^^^^\n" + + "Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" + + "----------\n" + + "2. ERROR in p2\\Y.java (at line 5)\n" + + " @Nullable @NonNull Object bar() {\n" + + " ^^^^^^^^\n" + + "Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" + + "----------\n" + + "3. ERROR in p2\\Y.java (at line 6)\n" + + " @NonNull @Nullable Object o = null;\n" + + " ^^^^^^^^^\n" + + "Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" + + "----------\n"); +} + // a nonnull variable is dereferenced in a loop public void test_nonnull_var_in_constrol_structure_1() { Map customOptions = getCompilerOptions(); @@ -2915,7 +3168,7 @@ customOptions, ""); } -// a nonnull variable is dereferenced method of a nested type +// a nonnull variable is dereferenced in a method of a nested type public void test_nesting_1() { Map customOptions = getCompilerOptions(); // customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); @@ -2924,7 +3177,6 @@ new String[] { "X.java", "import org.eclipse.jdt.annotation.*;\n" + - "@NonNullByDefault\n" + "public class X {\n" + " void print4(final String s1) {\n" + " for (int i=0; i<3; i++)\n" + @@ -2959,12 +3211,12 @@ }, customOptions, "----------\n" + - "1. ERROR in X.java (at line 16)\n" + + "1. ERROR in X.java (at line 15)\n" + " print(s2);\n" + " ^^\n" + "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + "----------\n" + - "2. ERROR in X.java (at line 25)\n" + + "2. ERROR in X.java (at line 24)\n" + " @NonNull String s3R = s3;\n" + " ^^\n" + "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java index 6b2f414..6a76255 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -1460,6 +1460,16 @@ int RedundantNullAnnotation = MethodRelated + 922; /** @since 3.8 */ int IllegalAnnotationForBaseType = TypeRelated + 923; + /** @since 3.8 */ + int RedundantNullDefaultAnnotation = Internal + 925; + /** @since 3.8 */ + int RedundantNullDefaultAnnotationPackage = Internal + 926; + /** @since 3.8 */ + int RedundantNullDefaultAnnotationType = Internal + 927; + /** @since 3.8 */ + int RedundantNullDefaultAnnotationMethod = Internal + 928; + /** @since 3.8 */ + int ContradictoryNullAnnotations = Internal + 929; /** * External problems -- These are problems defined by other plugins diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java index 68ed9eb..dde1d2f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java @@ -25,6 +25,8 @@ public abstract class Annotation extends Expression { final static MemberValuePair[] NoValuePairs = new MemberValuePair[0]; + private static final long TAGBITS_NULLABLE_OR_NONNULL = TagBits.AnnotationNullable|TagBits.AnnotationNonNull; + public int declarationSourceEnd; public Binding recipient; @@ -387,6 +389,10 @@ AbstractMethodDeclaration methodDeclaration = sourceType.scope.referenceContext.declarationOf(sourceMethod); recordSuppressWarnings(scope, methodDeclaration.declarationSourceStart, methodDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings); } + if ((sourceMethod.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) { + scope.problemReporter().contradictoryNullAnnotations(this); + sourceMethod.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems + } break; case Binding.FIELD : FieldBinding sourceField = (FieldBinding) this.recipient; @@ -404,6 +410,10 @@ LocalDeclaration localDeclaration = variable.declaration; recordSuppressWarnings(scope, localDeclaration.declarationSourceStart, localDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings); } + if ((variable.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) { + scope.problemReporter().contradictoryNullAnnotations(this); + variable.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems + } break; } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalTypeBinding.java index 3cf3183..686f7fd 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalTypeBinding.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,6 +13,7 @@ import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.CaseStatement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; @@ -85,6 +86,17 @@ return this.superclass; // default answer } +protected void checkRedundantNullnessDefaultRecurse(ASTNode location, Annotation[] annotations, long annotationTagBits) { + long outerDefault = this.enclosingMethod != null ? this.enclosingMethod.tagBits & ((TagBits.AnnotationNonNullByDefault|TagBits.AnnotationNullUnspecifiedByDefault)) : 0; + if (outerDefault != 0) { + if (outerDefault == annotationTagBits) { + this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this.enclosingMethod); + } + return; + } + super.checkRedundantNullnessDefaultRecurse(location, annotations, annotationTagBits); +} + public char[] computeUniqueKey(boolean isLeaf) { char[] outerKey = outermostEnclosingType().computeUniqueKey(isLeaf); int semicolon = CharOperation.lastIndexOf(';', outerKey); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java index 189497d..5c15b77 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -570,6 +570,13 @@ AbstractMethodDeclaration methodDecl = typeDecl.declarationOf(originalMethod); if (methodDecl != null) ASTNode.resolveAnnotations(methodDecl.scope, methodDecl.annotations, originalMethod); + long nullDefaultBits = this.tagBits & (TagBits.AnnotationNonNullByDefault|TagBits.AnnotationNullUnspecifiedByDefault); + if (nullDefaultBits != 0 && this.declaringClass instanceof SourceTypeBinding) { + SourceTypeBinding declaringSourceType = (SourceTypeBinding) this.declaringClass; + if (declaringSourceType.checkRedundantNullnessDefaultOne(methodDecl, methodDecl.annotations, nullDefaultBits)) { + declaringSourceType.checkRedundantNullnessDefaultRecurse(methodDecl, methodDecl.annotations, nullDefaultBits); + } + } } } return originalMethod.tagBits; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java index 4fc8606..d7c3181 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,9 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; public class NestedTypeBinding extends SourceTypeBinding { @@ -107,6 +110,17 @@ return synthLocal; } +protected void checkRedundantNullnessDefaultRecurse(ASTNode location, Annotation[] annotations, long annotationTagBits) { + ReferenceBinding currentType = this.enclosingType; + do { + if (!((SourceTypeBinding)currentType).checkRedundantNullnessDefaultOne(location, annotations, annotationTagBits)) { + return; + } + currentType = currentType.enclosingType(); + } while (currentType instanceof SourceTypeBinding); + super.checkRedundantNullnessDefaultRecurse(location, annotations, annotationTagBits); +} + /* Answer the receiver's enclosing type... null if the receiver is a top level type. */ public ReferenceBinding enclosingType() { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index c7ad46f..a0737c2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -23,6 +23,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; @@ -1636,11 +1637,47 @@ if (defaultAnnotation != null) { if (CharOperation.equals(this.sourceName, TypeConstants.PACKAGE_INFO_NAME)) { getPackage().nullnessDefaultAnnotation = defaultAnnotation; + long globalDefault = this.scope.compilerOptions().defaultNonNullness; + if (globalDefault == TagBits.AnnotationNonNull && (annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0) { + TypeDeclaration typeDecl = this.scope.referenceContext; + this.scope.problemReporter().nullDefaultAnnotationIsRedundant(typeDecl, typeDecl.annotations, null); + } } else { this.nullnessDefaultAnnotation = defaultAnnotation; + TypeDeclaration typeDecl = this.scope.referenceContext; + long nullDefaultBits = annotationTagBits & (TagBits.AnnotationNullUnspecifiedByDefault|TagBits.AnnotationNonNullByDefault); + checkRedundantNullnessDefaultRecurse(typeDecl, typeDecl.annotations, nullDefaultBits); } } } + +protected void checkRedundantNullnessDefaultRecurse(ASTNode location, Annotation[] annotations, long annotationTagBits) { + if (this.fPackage.nullnessDefaultAnnotation != null) { + if ((this.fPackage.nullnessDefaultAnnotation.id == TypeIds.T_ConfiguredAnnotationNonNull + && ((annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0))) { + this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this.fPackage); + } + return; + } + long globalDefault = this.scope.compilerOptions().defaultNonNullness; + if (globalDefault == TagBits.AnnotationNonNull && annotationTagBits == TagBits.AnnotationNonNullByDefault) { + this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, null); + } +} + +// return: should caller continue searching? +protected boolean checkRedundantNullnessDefaultOne(ASTNode location, Annotation[] annotations, long annotationTagBits) { + TypeBinding thisDefault = this.nullnessDefaultAnnotation; + if (thisDefault != null) { + if (thisDefault.id == TypeIds.T_ConfiguredAnnotationNonNull + && ((annotationTagBits & TagBits.AnnotationNonNullByDefault) != 0)) { + this.scope.problemReporter().nullDefaultAnnotationIsRedundant(location, annotations, this); + } + return false; // different default means inner default is not redundant -> we're done + } + return true; +} + private TypeBinding getNullnessDefaultAnnotation() { if (this.nullnessDefaultAnnotation instanceof UnresolvedReferenceBinding) this.nullnessDefaultAnnotation = this.scope.environment().getNullAnnotationResolved(this.nullnessDefaultAnnotation, this.scope); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 49bac9d..68f5e44 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -96,6 +96,7 @@ import org.eclipse.jdt.internal.compiler.lookup.InvocationSite; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; @@ -316,6 +317,10 @@ case IProblem.RequiredNonNullButProvidedUnknown: return CompilerOptions.NullSpecInsufficientInfo; case IProblem.RedundantNullAnnotation: + case IProblem.RedundantNullDefaultAnnotation: + case IProblem.RedundantNullDefaultAnnotationPackage: + case IProblem.RedundantNullDefaultAnnotationType: + case IProblem.RedundantNullDefaultAnnotationMethod: return CompilerOptions.RedundantNullAnnotation; case IProblem.BoxingConversion : @@ -8182,7 +8187,10 @@ .append(inheritedMethod.shortReadableName()); int sourceStart = methodDecl.returnType.sourceStart; Annotation[] annotations = methodDecl.annotations; - sourceStart = findAnnotationSourceStart(annotations, sourceStart, TypeIds.T_ConfiguredAnnotationNullable); + Annotation annotation = findAnnotation(annotations, TypeIds.T_ConfiguredAnnotationNullable); + if (annotation != null) { + sourceStart = annotation.sourceStart; + } this.handle( IProblem.IllegalReturnNullityRedefinition, new String[] { methodSignature.toString(), CharOperation.toString(nonNullAnnotationName)}, @@ -8242,7 +8250,8 @@ int sourceStart, sourceEnd; if (i == -1) { MethodDeclaration methodDecl = (MethodDeclaration) sourceMethod; - sourceStart = findAnnotationSourceStart(methodDecl.annotations, methodDecl.returnType.sourceStart, TypeIds.T_ConfiguredAnnotationNonNull); + Annotation annotation = findAnnotation(methodDecl.annotations, TypeIds.T_ConfiguredAnnotationNonNull); + sourceStart = annotation != null ? annotation.sourceStart : methodDecl.returnType.sourceStart; sourceEnd = methodDecl.returnType.sourceEnd; } else { Argument arg = sourceMethod.arguments[i]; @@ -8252,30 +8261,66 @@ this.handle(IProblem.RedundantNullAnnotation, ProblemHandler.NoArgument, ProblemHandler.NoArgument, sourceStart, sourceEnd); } +public void nullDefaultAnnotationIsRedundant(ASTNode location, Annotation[] annotations, Binding outer) { + Annotation annotation = findAnnotation(annotations, TypeIds.T_ConfiguredAnnotationNonNullByDefault); + int start = annotation != null ? annotation.sourceStart : location.sourceStart; + int end = annotation != null ? annotation.sourceEnd : location.sourceStart; + String[] args = NoArgument; + String[] shortArgs = NoArgument; + if (outer != null) { + args = new String[] { new String(outer.readableName()) }; + shortArgs = new String[] { new String(outer.shortReadableName()) }; + } + int problemId = IProblem.RedundantNullDefaultAnnotation; + if (outer instanceof PackageBinding) { + problemId = IProblem.RedundantNullDefaultAnnotationPackage; + } else if (outer instanceof ReferenceBinding) { + problemId = IProblem.RedundantNullDefaultAnnotationType; + } else if (outer instanceof MethodBinding) { + problemId = IProblem.RedundantNullDefaultAnnotationMethod; + } + this.handle(problemId, args, shortArgs, start, end); +} + +public void contradictoryNullAnnotations(Annotation annotation) { + // when this error is triggered we can safely assume that both annotations have been configured + char[][] nonNullAnnotationName = this.options.nonNullAnnotationName; + char[][] nullableAnnotationName = this.options.nullableAnnotationName; + String[] arguments = { + new String(CharOperation.concatWith(nonNullAnnotationName, '.')), + new String(CharOperation.concatWith(nullableAnnotationName, '.')) + }; + String[] shortArguments = { + new String(nonNullAnnotationName[nonNullAnnotationName.length-1]), + new String(nullableAnnotationName[nullableAnnotationName.length-1]) + }; + this.handle(IProblem.ContradictoryNullAnnotations, arguments, shortArguments, annotation.sourceStart, annotation.sourceEnd); +} + public void illegalAnnotationForBaseType(TypeReference type, Annotation[] annotations, char[] annotationName, long nullAnnotationTagBit) { int typeId = (nullAnnotationTagBit == TagBits.AnnotationNullable) ? TypeIds.T_ConfiguredAnnotationNullable : TypeIds.T_ConfiguredAnnotationNonNull; String[] args = new String[] { new String(annotationName), new String(type.getLastToken()) }; + Annotation annotation = findAnnotation(annotations, typeId); + int start = annotation != null ? annotation.sourceStart : type.sourceStart; this.handle(IProblem.IllegalAnnotationForBaseType, args, args, - findAnnotationSourceStart(annotations, type.sourceStart, typeId), + start, type.sourceEnd); } -private int findAnnotationSourceStart(Annotation[] annotations, int startFallback, int typeId) { - int sourceStart = startFallback; +private Annotation findAnnotation(Annotation[] annotations, int typeId) { if (annotations != null) { // should have a @NonNull/@Nullable annotation, search for it: int length = annotations.length; for (int j=0; j