Upgrade JDT in AspectJ to Mars.2 R4_5_Maintenance branch #A7BBA8B15
diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF index 16c0394..21a1907 100644 --- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.core; singleton:=true -Bundle-Version: 3.11.0.qualifier +Bundle-Version: 3.11.2.qualifier Bundle-Activator: org.eclipse.jdt.core.JavaCore Bundle-Vendor: %providerName Bundle-Localization: plugin
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties index 1c362ea..d4efab1 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -26,7 +26,7 @@ #Format: compiler.name = word1 word2 word3 compiler.name = Eclipse Compiler for Java(TM) #Format: compiler.version = (The place holders will be automatically filled. Do not remove or alter it) -compiler.version = MARS_1007a0215, 3.10.0 +compiler.version = MARS_A7BBA8B15, 3.11.2 compiler.copyright = Copyright IBM Corp 2000, 2015. All rights reserved. ### progress
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java index 0572ffc..7ce5155 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -5303,6 +5303,7 @@ char[] typeName = currentType.qualifiedSourceName(); InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition); + proposal.setBinding(constructor); proposal.setDeclarationSignature(getSignature(currentType)); proposal.setDeclarationKey(currentType.computeUniqueKey()); proposal.setSignature(getSignature(constructor)); @@ -5463,6 +5464,7 @@ char[] typeName = currentType.qualifiedSourceName(); int constructorRelevance = relevance + computeRelevanceForConstructor(); InternalCompletionProposal proposal = createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition); + proposal.setBinding(constructor); proposal.setDeclarationSignature(getSignature(currentType)); proposal.setSignature(getSignature(constructor)); MethodBinding original = constructor.original(); @@ -5506,6 +5508,7 @@ } else { if(!isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(constructor); proposal.setDeclarationSignature(getSignature(currentType)); proposal.setSignature(getSignature(constructor)); MethodBinding original = constructor.original(); @@ -5547,6 +5550,7 @@ if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) { char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK); InternalCompletionProposal proposal = createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(constructor); proposal.setDeclarationSignature(getSignature(currentType)); proposal.setSignature(getSignature(constructor)); MethodBinding original = constructor.original(); @@ -5790,6 +5794,7 @@ if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -5843,6 +5848,7 @@ ReferenceBinding fieldType = (ReferenceBinding)field.type; InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -6184,6 +6190,7 @@ this.noProposal = false; if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(constructor); proposal.setDeclarationSignature(getSignature(currentType)); proposal.setSignature(getSignature(constructor)); MethodBinding original = constructor.original(); @@ -6417,6 +6424,7 @@ // Standard proposal if (!this.isIgnored(CompletionProposal.FIELD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) { InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -6451,6 +6459,7 @@ if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_FIELD_REF)) { char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK); InternalCompletionProposal proposal = createProposal(CompletionProposal.JAVADOC_FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -6493,6 +6502,7 @@ } else { if(!this.isIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, missingElements != null)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setReceiverSignature(getSignature(receiverType)); @@ -7571,6 +7581,7 @@ char[] completion = CharOperation.concat(receiverType.sourceName, field.name, '.'); InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -7613,6 +7624,7 @@ char[] completion = field.name; InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -7847,6 +7859,7 @@ this.noProposal = false; if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition); + proposal.setBinding(field); proposal.setDeclarationSignature(getSignature(field.declaringClass)); proposal.setSignature(getSignature(field.type)); proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName()); @@ -8445,6 +8458,7 @@ this.noProposal = false; if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_DECLARATION, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setDeclarationKey(method.declaringClass.computeUniqueKey()); proposal.setSignature(getSignature(method)); @@ -8724,6 +8738,7 @@ // Standard proposal if(!this.isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) { InternalCompletionProposal proposal = createProposal(completionOnReferenceExpressionName ? CompletionProposal.METHOD_NAME_REFERENCE : CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -8765,6 +8780,7 @@ if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) { char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK); InternalCompletionProposal proposal = createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -8793,6 +8809,7 @@ } else { if(!this.isIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, missingElements != null)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -8983,6 +9000,7 @@ completion = CharOperation.concat(receiverType.sourceName, completion, '.'); InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -9012,6 +9030,7 @@ completion = CharOperation.concat(receiverType.sourceName, completion, '.'); InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -9059,6 +9078,7 @@ } else { if (!this.isIgnored(CompletionProposal.METHOD_REF, CompletionProposal.METHOD_IMPORT)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original(); @@ -9211,6 +9231,7 @@ this.noProposal = false; if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) { InternalCompletionProposal proposal = createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition); + proposal.setBinding(method); proposal.setDeclarationSignature(getSignature(method.declaringClass)); proposal.setSignature(getSignature(method)); MethodBinding original = method.original();
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java index 9e324c5..b2427c4 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalCompletionProposal.java
@@ -29,6 +29,9 @@ import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.core.BinaryType; import org.eclipse.jdt.internal.core.JavaElement; import org.eclipse.jdt.internal.core.JavaModelManager; @@ -134,6 +137,13 @@ private char[] name = null; /** + * Binding of the method or constructor being proposed, or + * <code>null</code> if none. + * Defaults to null. + */ + private Binding binding = null; + + /** * Signature of the method, field type, member type, * relevant in the context, or <code>null</code> if none. * Defaults to null. @@ -907,6 +917,45 @@ } /** + * Returns a binding of the method or field corresponding to this proposal or <code>null</code> if none. + * <p> + * The binding <em>may</em> be available for the following kinds of completion proposals: + * <ul> + * <li><code>ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION</code> - {@link MethodBinding} + * of the constructor being proposed</li> + * <li><code>CONSTRUCTOR_INVOCATION</code> - {@link MethodBinding} + * of the constructor being proposed</li> + * <li><code>FIELD_REF</code> - {@link FieldBinding} + * of the field being proposed</li> + * <li><code>FIELD_REF_WITH_CASTED_RECEIVER</code> - {@link FieldBinding} + * of the field being proposed</li> + * <li><code>JAVADOC_FIELD_REF</code> - {@link FieldBinding} + * of the field being proposed</li> + * <li><code>JAVADOC_METHOD_REF</code> - {@link MethodBinding} + * of the method or constructor being proposed</li> + * <li><code>METHOD_DECLARATION</code> - {@link MethodBinding} + * of the method or constructor being proposed</li> + * <li><code>METHOD_NAME_REFERENCE</code> - {@link MethodBinding} + * of the method or constructor being proposed</li> + * <li><code>METHOD_REF</code> - {@link MethodBinding} + * of the method or constructor being proposed</li> + * <li><code>METHOD_REF_WITH_CASTED_RECEIVER</code> - {@link MethodBinding} + * of the method or constructor being proposed</li> + * </ul> + * For other kinds of completion proposals, this method returns <code>null</code>. + * </p> + * + * @return the binding corresponding to this proposal (if available), or <code>null</code> if none + */ + public Binding getBinding() { + return this.binding; + } + + public void setBinding(Binding binding) { + this.binding = binding; + } + + /** * Returns the signature of the method or type * relevant in the context, or <code>null</code> if none. * <p>
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java index 6d049f8..0d4d89b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
@@ -273,7 +273,7 @@ // perform some emulation work in case there is some and we are inside a local type only if (allocatedTypeErasure.isNestedType() - && (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaScope())) { + && (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaSubscope())) { if (allocatedTypeErasure.isLocalType()) { ((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, false);
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 23fd4a3..94b8b08 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
@@ -21,6 +21,7 @@ * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations * Bug 457210 - [1.8][compiler][null] Wrong Nullness errors given on full build build but not on incremental build? + * Bug 469584 - ClassCastException in Annotation.detectStandardAnnotation (320) * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for * Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work) * Bug 409517 - [1.8][compiler] Type annotation problems on more elaborate array references @@ -319,13 +320,11 @@ case TypeIds.T_JavaLangAnnotationRetention : if (valueAttribute != null) { Expression expr = valueAttribute.value; - if ((expr.bits & Binding.VARIABLE) == Binding.FIELD) { - if (expr instanceof Reference) { // New AspectJ Extension (pr148537) + if ((expr.bits & Binding.VARIABLE) == Binding.FIELD && expr instanceof Reference) { // anything but Reference would be a type error anyway FieldBinding field = ((Reference)expr).fieldBinding(); if (field != null && field.declaringClass.id == T_JavaLangAnnotationRetentionPolicy) { tagBits |= getRetentionPolicy(field.name); } - } // New AspectJ Extension - end of if() } } break;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java index 4f9cd46..cc61fc0 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java index 89f4b72..1beec31 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
@@ -34,6 +34,7 @@ import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; @@ -424,6 +425,11 @@ TypeBinding expectedCollectionType = null; if (elementType != null && collectionType != null) { boolean isTargetJsr14 = this.scope.compilerOptions().targetJDK == ClassFileConstants.JDK1_4; + if (collectionType.isCapture()) { + TypeBinding upperBound = ((CaptureBinding)collectionType).firstBound; + if (upperBound.isArrayType()) + collectionType = upperBound; // partially anticipating the fix for https://bugs.openjdk.java.net/browse/JDK-8013843 + } if (collectionType.isArrayType()) { // for(E e : E[]) this.kind = ARRAY; this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java index 8fc1d41..a615681 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2014 IBM Corporation and others. + * Copyright (c) 2012, 2015 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 @@ -36,6 +36,7 @@ * Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression * Bug 453483 - [compiler][null][loop] Improve null analysis for loops * Bug 455723 - Nonnull argument not correctly inferred in loop + * Bug 463728 - [1.8][compiler][inference] Ternary operator in lambda derives wrong type * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for * Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas *******************************************************************************/ @@ -424,6 +425,8 @@ if (this.body instanceof Expression) { Expression expression = (Expression) this.body; new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd, true).resolve(this.scope); // :-) ;-) + if (expression.resolvedType == TypeBinding.VOID && !expression.statementExpression()) + this.scope.problemReporter().invalidExpressionAsStatement(expression); } else { this.body.resolve(this.scope); /* At this point, shape analysis is complete for ((see returnsExpression(...)) @@ -981,13 +984,11 @@ this.returnsValue = true; this.voidCompatible = false; this.valueCompatible = !this.returnsVoid; - if (resultType != null) { - Expression [] returnExpressions = this.resultExpressions; - int resultsLength = returnExpressions.length; - System.arraycopy(returnExpressions, 0, returnExpressions = new Expression[resultsLength + 1], 0, resultsLength); - returnExpressions[resultsLength] = expression; - this.resultExpressions = returnExpressions; - } + Expression [] returnExpressions = this.resultExpressions; + int resultsLength = returnExpressions.length; + System.arraycopy(returnExpressions, 0, returnExpressions = new Expression[resultsLength + 1], 0, resultsLength); + returnExpressions[resultsLength] = expression; + this.resultExpressions = returnExpressions; } else { this.returnsVoid = true; this.valueCompatible = false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java index 7f1eb02..c738446 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -52,6 +52,7 @@ * Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression * Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief * Bug 407414 - [compiler][null] Incorrect warning on a primitive type being null + * Bug 470958 - [1.8] Unable to convert lambda * Jesper S Moller - Contributions for * Bug 378674 - "The method can be declared as static" is wrong * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for @@ -82,6 +83,7 @@ import org.eclipse.jdt.internal.compiler.lookup.IPrivilegedHandler; import org.eclipse.jdt.internal.compiler.lookup.ImplicitNullAnnotationVerifier; import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18; +import org.eclipse.jdt.internal.compiler.lookup.InferenceVariable; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding; @@ -472,7 +474,7 @@ codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessor, null /* default declaringClass */, this.typeArguments); } else { codeStream.invoke(Opcodes.OPC_invokevirtual, this.syntheticAccessor, null /* default declaringClass */, this.typeArguments); - } + } // End AspectJ extension } // required cast must occur even if no value is required @@ -663,6 +665,9 @@ // scope.problemReporter().genericInferenceError("Receiver was unexpectedly found resolved", this); //$NON-NLS-1$ // AspectJ Extension: End this.actualReceiverType = this.receiver.resolveType(scope); + if (this.actualReceiverType instanceof InferenceVariable) { + return null; // not yet ready for resolving + } this.receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0; if (receiverCast && this.actualReceiverType != null) { // due to change of declaring class with receiver type, only identity cast should be notified
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java index 8f590f7..ccaa1ce 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
@@ -85,6 +85,7 @@ public static int checkAssignment(BlockScope currentScope, FlowContext flowContext, VariableBinding var, FlowInfo flowInfo, int nullStatus, Expression expression, TypeBinding providedType) { + if (providedType == null) return FlowInfo.UNKNOWN; // assume we already reported an error long lhsTagBits = 0L; boolean hasReported = false; if (!currentScope.environment().usesNullTypeAnnotations()) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java index ff66190..416812e 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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 @@ -256,7 +256,7 @@ // perform some extra emulation work in case there is some and we are inside a local type only if (allocatedTypeErasure.isNestedType() - && (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaScope())) { + && (currentScope.enclosingSourceType().isLocalType() || currentScope.isLambdaSubscope())) { if (allocatedTypeErasure.isLocalType()) { ((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, this.enclosingInstance != null);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java index 6df8946..2343f42 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -36,6 +36,7 @@ * Bug 448709 - [1.8][null] ensure we don't infer types that violate null constraints on a type parameter's bound * Bug 459967 - [null] compiler should know about nullness of special methods like MyEnum.valueOf() * Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param + * Bug 470542 - NullPointerException in ReferenceExpression.isPotentiallyCompatibleWith (962) * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contribution for * Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work) *******************************************************************************/ @@ -843,7 +844,11 @@ return null; int n = functionType.parameters.length; int k = this.exactMethodBinding.parameters.length; - return (n == k || n == k + 1) ? this : null; + + if (!this.haveReceiver && this.isMethodReference() && !this.exactMethodBinding.isStatic()) { + k++; + } + return (n == k) ? this : null; } // descriptors parameters should be free of inference variables. ReferenceExpression copy = cachedResolvedCopy(targetType); @@ -959,12 +964,16 @@ public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope scope) { final boolean isConstructorRef = isConstructorReference(); - if (isConstructorRef && this.receiverType.isArrayType()) { + if (isConstructorRef) { + if (this.receiverType == null) + return false; + if (this.receiverType.isArrayType()) { final TypeBinding leafComponentType = this.receiverType.leafComponentType(); if (!leafComponentType.isReifiable()) { return false; } } + } // We get here only when the reference expression is NOT pertinent to applicability. if (!super.isPertinentToApplicability(targetType, null))
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java index 07cd801..f8fc493 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java index ed80d9b..aafe24b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java
@@ -109,6 +109,12 @@ } @Override + public ITypeAnnotationWalker toSupertype(short index, char[] superTypeSignature) { + if (this.isEmpty) return restrict(this.matches, this.pathPtr); + return super.toSupertype(index, superTypeSignature); + } + + @Override public ITypeAnnotationWalker toMethodParameter(short index) { // don't set nextIsDefaultLocation, because signature-level nullness is handled by ImplicitNullAnnotationVerifier if (this.isEmpty) return restrict(this.matches, this.pathPtr); @@ -131,6 +137,14 @@ } @Override + public ITypeAnnotationWalker toWildcardBound() { + this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0; + this.nextIsTypeBound = true; + if (this.isEmpty) return restrict(this.matches, this.pathPtr); + return super.toWildcardBound(); + } + + @Override public ITypeAnnotationWalker toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) { this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0; this.nextIsTypeBound = true; @@ -155,6 +169,12 @@ } @Override + protected ITypeAnnotationWalker toNextDetail(int detailKind) { + if (this.isEmpty) return restrict(this.matches, this.pathPtr); + return super.toNextDetail(detailKind); + } + + @Override public IBinaryAnnotation[] getAnnotationsAtCursor(int currentTypeId) { IBinaryAnnotation[] normalAnnotations = this.isEmpty ? null : super.getAnnotationsAtCursor(currentTypeId); if (this.atDefaultLocation &&
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java index d0cf15e..96ada5b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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 @@ -65,10 +65,6 @@ public static final int LABELS_INCREMENT = 5; // local variable attributes output public static final int LOCALS_INCREMENT = 10; - static ExceptionLabel[] noExceptionHandlers = new ExceptionLabel[LABELS_INCREMENT]; - static BranchLabel[] noLabels = new BranchLabel[LABELS_INCREMENT]; - static LocalVariableBinding[] noLocals = new LocalVariableBinding[LOCALS_INCREMENT]; - static LocalVariableBinding[] noVisibleLocals = new LocalVariableBinding[LOCALS_INCREMENT]; public static final CompilationResult RESTART_IN_WIDE_MODE = new CompilationResult((char[])null, 0, 0, 0); public static final CompilationResult RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE = new CompilationResult((char[])null, 0, 0, 0); @@ -4115,32 +4111,12 @@ this.startingClassFileOffset = this.classFileOffset; this.pcToSourceMapSize = 0; this.lastEntryPC = 0; - int length = this.visibleLocals.length; - if (noVisibleLocals.length < length) { - noVisibleLocals = new LocalVariableBinding[length]; - } - System.arraycopy(noVisibleLocals, 0, this.visibleLocals, 0, length); this.visibleLocalsCount = 0; - length = this.locals.length; - if (noLocals.length < length) { - noLocals = new LocalVariableBinding[length]; - } - System.arraycopy(noLocals, 0, this.locals, 0, length); this.allLocalsCounter = 0; - length = this.exceptionLabels.length; - if (noExceptionHandlers.length < length) { - noExceptionHandlers = new ExceptionLabel[length]; - } - System.arraycopy(noExceptionHandlers, 0, this.exceptionLabels, 0, length); this.exceptionLabelsCounter = 0; - length = this.labels.length; - if (noLabels.length < length) { - noLabels = new BranchLabel[length]; - } - System.arraycopy(noLabels, 0, this.labels, 0, length); this.countLabels = 0; this.lastAbruptCompletion = -1;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java index 02070bd..cfd1fa7 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/AnnotationBinding.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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 @@ -245,7 +245,12 @@ } public int hashCode() { - return this.type.hashCode(); + int result = 17; + int c = this.type.hashCode(); + result = 31 * result + c; + c = Arrays.hashCode(this.pairs); + result = 31 * result + c; + return result; } public boolean equals(Object object) { if (this == object)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java index edefccf..67a5b6a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -34,6 +34,7 @@ * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations * Bug 453475 - [1.8][null] Contradictory null annotations (4.5 M3 edition) * Bug 454182 - Internal compiler error when using 1.8 compliance for simple project + * Bug 447661 - [1.8][null] Incorrect 'expression needs unchecked conversion' warning * Jesper Steen Moller - Contributions for * Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing * Bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable @@ -412,7 +413,7 @@ // need annotations on the type before processing null annotations on members respecting any @NonNullByDefault: scanTypeForNullDefaultAnnotation(binaryType, this.fPackage); } - ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryType.getTypeAnnotations()); + ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryType.getTypeAnnotations(), Binding.NO_NULL_DEFAULT); ITypeAnnotationWalker toplevelWalker = binaryType.enrichWithExternalAnnotationsFor(walker, null, this.environment); char[] typeSignature = binaryType.getGenericSignature(); // use generic signature even in 1.4 this.tagBits |= binaryType.getTagBits(); @@ -533,24 +534,38 @@ } } -private ITypeAnnotationWalker getTypeAnnotationWalker(IBinaryTypeAnnotation[] annotations) { +/* When creating a method we need to pass in any default 'nullness' from a @NNBD immediately on this method. */ +private ITypeAnnotationWalker getTypeAnnotationWalker(IBinaryTypeAnnotation[] annotations, int nullness) { if (!isPrototype()) throw new IllegalStateException(); if (annotations == null || annotations.length == 0 || !this.environment.usesAnnotatedTypeSystem()) { if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) { - int nullness = getNullDefault(); + if (nullness == Binding.NO_NULL_DEFAULT) + nullness = getNullDefault(); if (nullness > Binding.NULL_UNSPECIFIED_BY_DEFAULT) return new NonNullDefaultAwareTypeAnnotationWalker(nullness, this.environment); } return ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER; } if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) { - int nullness = getNullDefault(); + if (nullness == Binding.NO_NULL_DEFAULT) + nullness = getNullDefault(); if (nullness > Binding.NULL_UNSPECIFIED_BY_DEFAULT) return new NonNullDefaultAwareTypeAnnotationWalker(annotations, nullness, this.environment); } return new TypeAnnotationWalker(annotations); } +private int getNullDefaultFrom(IBinaryAnnotation[] declAnnotations) { + if (declAnnotations != null) { + char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName(); + for (IBinaryAnnotation annotation : declAnnotations) { + char[][] typeName = signature2qualifiedTypeName(annotation.getTypeName()); + if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) + return getNonNullByDefaultValue(annotation); + } + } + return Binding.NO_NULL_DEFAULT; +} private void createFields(IBinaryField[] iFields, IBinaryType binaryType, long sourceLevel, char[][][] missingTypeNames) { if (!isPrototype()) throw new IllegalStateException(); @@ -565,7 +580,7 @@ for (int i = 0; i < size; i++) { IBinaryField binaryField = iFields[i]; char[] fieldSignature = use15specifics ? binaryField.getGenericSignature() : null; - ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryField.getTypeAnnotations()); + ITypeAnnotationWalker walker = getTypeAnnotationWalker(binaryField.getTypeAnnotations(), Binding.NO_NULL_DEFAULT); if (sourceLevel >= ClassFileConstants.JDK1_8) { // below 1.8, external annotations will be attached later walker = binaryType.enrichWithExternalAnnotationsFor(walker, iFields[i], this.environment); } @@ -637,7 +652,7 @@ variables properly in order to be able to apply substitutions and thus be able to detect overriding in the presence of generics. Seeing the erased form is not good enough. */ - ITypeAnnotationWalker walker = getTypeAnnotationWalker(method.getTypeAnnotations()); + ITypeAnnotationWalker walker = getTypeAnnotationWalker(method.getTypeAnnotations(), getNullDefaultFrom(method.getAnnotations())); char[] methodSignature = method.getGenericSignature(); // always use generic signature, even in 1.4 if (methodSignature == null) { // no generics char[] methodDescriptor = method.getMethodDescriptor(); // of the form (I[Ljava/jang/String;)V @@ -1593,7 +1608,7 @@ char[] annotationTypeName = annotations[i].getTypeName(); if (annotationTypeName[0] != Util.C_RESOLVED) continue; - char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' + char[][] typeName = signature2qualifiedTypeName(annotationTypeName); if (CharOperation.equals(typeName, nonNullAnnotationName)) { fieldBinding.tagBits |= TagBits.AnnotationNonNull; explicitNullness = true; @@ -1629,7 +1644,7 @@ char[] annotationTypeName = annotations[i].getTypeName(); if (annotationTypeName[0] != Util.C_RESOLVED) continue; - char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' + char[][] typeName = signature2qualifiedTypeName(annotationTypeName); if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) { methodBinding.defaultNullness = getNonNullByDefaultValue(annotations[i]); if (methodBinding.defaultNullness == Binding.NULL_UNSPECIFIED_BY_DEFAULT) @@ -1663,7 +1678,7 @@ char[] annotationTypeName = paramAnnotations[i].getTypeName(); if (annotationTypeName[0] != Util.C_RESOLVED) continue; - char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' + char[][] typeName = signature2qualifiedTypeName(annotationTypeName); if (CharOperation.equals(typeName, nonNullAnnotationName)) { if (methodBinding.parameterNonNullness == null) methodBinding.parameterNonNullness = new Boolean[numVisibleParams]; @@ -1701,7 +1716,7 @@ char[] annotationTypeName = annotations[i].getTypeName(); if (annotationTypeName[0] != Util.C_RESOLVED) continue; - char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' + char[][] typeName = signature2qualifiedTypeName(annotationTypeName); if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) { // using NonNullByDefault we need to inspect the details of the value() attribute: nullness = getNonNullByDefaultValue(annotations[i]); @@ -1767,7 +1782,7 @@ // pre: null annotation analysis is enabled int getNonNullByDefaultValue(IBinaryAnnotation annotation) { char[] annotationTypeName = annotation.getTypeName(); - char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' + char[][] typeName = signature2qualifiedTypeName(annotationTypeName); IBinaryElementValuePair[] elementValuePairs = annotation.getElementValuePairs(); if (elementValuePairs == null || elementValuePairs.length == 0 ) { // no argument: apply default default @@ -1793,6 +1808,10 @@ } } +private char[][] signature2qualifiedTypeName(char[] typeSignature) { + return CharOperation.splitOn('/', typeSignature, 1, typeSignature.length-1); // cut off leading 'L' and trailing ';' +} + @Override int getNullDefault() { return this.defaultNullness;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java index e75257a..dc660b7 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java
@@ -23,6 +23,7 @@ import org.eclipse.jdt.internal.compiler.ast.ExpressionContext; import org.eclipse.jdt.internal.compiler.ast.Invocation; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.SuspendedInferenceRecord; @@ -67,8 +68,11 @@ } if (!canBePolyExpression(this.left)) { TypeBinding exprType = this.left.resolvedType; - if (exprType == null || !exprType.isValidBinding()) + if (exprType == null || !exprType.isValidBinding()) { + if (this.left instanceof MessageSend && ((MessageSend)this.left).actualReceiverType instanceof InferenceVariable) + return null; // nothing valuable to infer from this return FALSE; + } return ConstraintTypeFormula.create(exprType, this.right, COMPATIBLE, this.isSoft); } else { // shapes of poly expressions (18.2.1)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java index 5ea33a7..360a5ab 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java
@@ -64,14 +64,10 @@ return this.left.isCompatibleWith(this.right, inferenceContext.scope) || this.left.isBoxingCompatibleWith(this.right, inferenceContext.scope) ? TRUE : FALSE; } if (this.left.isPrimitiveType()) { - if (inferenceContext.inferenceKind == InferenceContext18.CHECK_STRICT) - inferenceContext.inferenceKind = InferenceContext18.CHECK_LOOSE; TypeBinding sPrime = inferenceContext.environment.computeBoxingType(this.left); return ConstraintTypeFormula.create(sPrime, this.right, COMPATIBLE, this.isSoft); } if (this.right.isPrimitiveType()) { - if (inferenceContext.inferenceKind == InferenceContext18.CHECK_STRICT) - inferenceContext.inferenceKind = InferenceContext18.CHECK_LOOSE; TypeBinding tPrime = inferenceContext.environment.computeBoxingType(this.right); return ConstraintTypeFormula.create(this.left, tPrime, SAME, this.isSoft); }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java index 0e06744..7688a6c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
@@ -14,6 +14,7 @@ * Bug 447088 - [null] @Nullable on fully qualified field type is ignored * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations * Bug 458396 - NPE in CodeStream.invoke() + * Bug 446217 - [null] @NonNullByDefault in package-info.java causes bogus "null type safety" warning *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; @@ -245,8 +246,11 @@ public void fillInDefaultNonNullness(FieldDeclaration sourceField, Scope scope) { LookupEnvironment environment = scope.environment(); - if ( this.type != null - && !this.type.isBaseType() + if (this.type == null) + return; + if (environment.usesNullTypeAnnotations() && !this.type.acceptsNonNullDefault()) + return; + if ( !this.type.isBaseType() && (this.tagBits & TagBits.AnnotationNullMASK) == 0 // declaration annotation? && (this.type.tagBits & TagBits.AnnotationNullMASK) == 0) // type annotation? (java.lang.@Nullable String) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java index 384b4b5..4057cee 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -269,7 +269,7 @@ break returnType; // compatible by construction, skip complain phase below } } - if (hasReturnNonNullDefault && currentMethod.returnType.acceptsNonNullDefault()) { // conflict with inheritance already checked + if (hasReturnNonNullDefault && (!useTypeAnnotations || currentMethod.returnType.acceptsNonNullDefault())) { // conflict with inheritance already checked currentNullnessBits = TagBits.AnnotationNonNull; applyReturnNullBits(currentMethod, currentNullnessBits); }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index e28b06d..dd33dbb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
@@ -493,6 +493,10 @@ if (withWildCards != null) { t = ConstraintExpressionFormula.findGroundTargetType(this, skope, lambda, withWildCards); } + if (!t.isProperType(true) && t.isParameterizedType()) { + // prevent already resolved inference variables from leaking into the lambda + t = (ReferenceBinding) Scope.substitute(getResultSubstitution(this.currentBounds, false), t); + } MethodBinding functionType; if (t != null && (functionType = t.getSingleAbstractMethod(skope, true)) != null && (lambda = lambda.resolveExpressionExpecting(t, this.scope, this)) != null) { TypeBinding r = functionType.returnType; @@ -1106,14 +1110,29 @@ * on any uninstantiated variable outside the set. */ private Set<InferenceVariable> getSmallestVariableSet(BoundSet bounds, InferenceVariable[] subSet) { + // "Given a set of inference variables to resolve, let V be the union of this set and + // all variables upon which the resolution of at least one variable in this set depends." + Set<InferenceVariable> v = new HashSet<InferenceVariable>(); + Map<InferenceVariable,Set<InferenceVariable>> dependencies = new HashMap<>(); // compute only once, store for the final loop over 'v'. + for (InferenceVariable iv : subSet) { + Set<InferenceVariable> tmp = new HashSet<>(); + addDependencies(bounds, tmp, iv); + dependencies.put(iv, tmp); + v.addAll(tmp); + } + // "If every variable in V has an instantiation, then resolution succeeds and this procedure terminates." + // -> (implicit if result remains unassigned) + // "Otherwise, let { α1, ..., αn } be a non-empty subset of uninstantiated variables in V such that ... int min = Integer.MAX_VALUE; Set<InferenceVariable> result = null; - for (int i = 0; i < subSet.length; i++) { - InferenceVariable currentVariable = subSet[i]; + // "i) for all i (1 ≤ i ≤ n), ..." + for (InferenceVariable currentVariable : v) { if (!bounds.isInstantiated(currentVariable)) { - Set<InferenceVariable> set = new HashSet<InferenceVariable>(); - if (!addDependencies(bounds, set, currentVariable, min)) - continue; + // "... if αi depends on the resolution of a variable β, then either β has an instantiation or there is some j such that β = αj; ..." + Set<InferenceVariable> set = dependencies.get(currentVariable); + if (set == null) // not an element of the original subSet, still need to fetch this var's dependencies + addDependencies(bounds, set = new HashSet<>(), currentVariable); + // "... and ii) there exists no non-empty proper subset of { α1, ..., αn } with this property." int cur = set.size(); if (cur == 1) return set; // won't get smaller @@ -1126,19 +1145,15 @@ return result; } - private boolean addDependencies(BoundSet boundSet, Set<InferenceVariable> variableSet, InferenceVariable currentVariable, int min) { - if (variableSet.size() >= min) - return false; // no improvement - if (boundSet.isInstantiated(currentVariable)) return true; // not added - if (!variableSet.add(currentVariable)) return true; // already present + private void addDependencies(BoundSet boundSet, Set<InferenceVariable> variableSet, InferenceVariable currentVariable) { + if (boundSet.isInstantiated(currentVariable)) return; // not added + if (!variableSet.add(currentVariable)) return; // already present for (int j = 0; j < this.inferenceVariables.length; j++) { InferenceVariable nextVariable = this.inferenceVariables[j]; if (TypeBinding.equalsEquals(nextVariable, currentVariable)) continue; if (boundSet.dependsOnResolutionOf(currentVariable, nextVariable)) - if (!addDependencies(boundSet, variableSet, nextVariable, min)) - return false; // abort traversal: no improvement + addDependencies(boundSet, variableSet, nextVariable); } - return true; } private ConstraintFormula pickFromCycle(Set<ConstraintFormula> c) { @@ -1398,7 +1413,7 @@ this.usesUncheckedConversion = record.usesUncheckedConversion; } - private Substitution getResultSubstitution(final BoundSet result) { + private Substitution getResultSubstitution(final BoundSet result, final boolean full) { return new Substitution() { public LookupEnvironment environment() { return InferenceContext18.this.environment; @@ -1408,7 +1423,9 @@ } public TypeBinding substitute(TypeVariableBinding typeVariable) { if (typeVariable instanceof InferenceVariable) { - return result.getInstantiation((InferenceVariable) typeVariable, InferenceContext18.this.environment); + TypeBinding instantiation = result.getInstantiation((InferenceVariable) typeVariable, InferenceContext18.this.environment); + if (instantiation != null || full) + return instantiation; } return typeVariable; } @@ -1533,7 +1550,7 @@ void reportUncheckedConversions(BoundSet solution) { if (this.constraintsWithUncheckedConversion != null) { int len = this.constraintsWithUncheckedConversion.size(); - Substitution substitution = getResultSubstitution(solution); + Substitution substitution = getResultSubstitution(solution, true); for (int i = 0; i < len; i++) { ConstraintTypeFormula constraint = (ConstraintTypeFormula) this.constraintsWithUncheckedConversion.get(i); TypeBinding expectedType = constraint.right;
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 01aac01..57a81d9 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
@@ -25,6 +25,8 @@ * Bug 443347 - [1.8][null] @NonNullByDefault should not affect constructor arguments of an anonymous instantiation * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations * Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param + * Bug 456584 - [1.8][null] Bogus warning for return type variable's @NonNull annotation being 'redundant' + * Bug 471611 - Error on hover on call to generic method with null annotation * Jesper Steen Moller - Contributions for * Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing *******************************************************************************/ @@ -589,8 +591,8 @@ if (added) this.tagBits |= TagBits.HasParameterAnnotations; } - if (this.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true)) { - if (this.returnType.acceptsNonNullDefault() && (this.returnType.tagBits & TagBits.AnnotationNullMASK) == 0) { + if (this.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true) && this.returnType.acceptsNonNullDefault()) { + if ((this.returnType.tagBits & TagBits.AnnotationNullMASK) == 0) { this.returnType = env.createAnnotatedType(this.returnType, new AnnotationBinding[]{env.getNonNullAnnotation()}); } else if (sourceMethod instanceof MethodDeclaration && (this.returnType.tagBits & TagBits.AnnotationNonNull) != 0 && ((MethodDeclaration)sourceMethod).hasNullTypeAnnotation(AnnotationPosition.MAIN_TYPE)) { @@ -742,7 +744,7 @@ if (this.declaringClass instanceof SourceTypeBinding) { SourceTypeBinding sourceType = (SourceTypeBinding) this.declaringClass; if (sourceType.scope != null) { - AbstractMethodDeclaration methodDecl = sourceType.scope.referenceType().declarationOf(this); + AbstractMethodDeclaration methodDecl = sourceType.scope.referenceType().declarationOf(originalMethod); for (int i = 0; i < length; i++) { Argument argument = methodDecl.arguments[i]; if (argument.annotations != null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java index 32b1b07..81d184f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -95,7 +95,13 @@ this.rank = prototype.rank; this.firstBound = prototype.firstBound; this.superclass = prototype.superclass; - this.superInterfaces = prototype.superInterfaces; + if (prototype.superInterfaces != null) { + int len = prototype.superInterfaces.length; + if (len > 0) + System.arraycopy(prototype.superInterfaces, 0, this.superInterfaces = new ReferenceBinding[len], 0, len); + else + this.superInterfaces = Binding.NO_SUPERINTERFACES; + } this.genericTypeSignature = prototype.genericTypeSignature; this.environment = prototype.environment; prototype.tagBits |= TagBits.HasAnnotatedVariants; @@ -244,13 +250,11 @@ } public int boundsCount() { - if (this.firstBound == null) { + if (this.firstBound == null) return 0; - } else if (TypeBinding.equalsEquals(this.firstBound, this.superclass)) { - return this.superInterfaces.length + 1; - } else { - return this.superInterfaces.length; - } + if (this.firstBound.isInterface()) + return this.superInterfaces.length; // only interface bounds + return this.superInterfaces.length + 1; // class or array type isn't contained in superInterfaces } /** @@ -421,10 +425,11 @@ if (n == 0) return NO_TYPE_BOUNDS; TypeBound[] bounds = new TypeBound[n]; - bounds[0] = TypeBound.createBoundOrDependency(theta, this.firstBound, variable); - int ifcOffset = TypeBinding.equalsEquals(this.firstBound, this.superclass) ? -1 : 0; - for (int i = 1; i < n; i++) - bounds[i] = TypeBound.createBoundOrDependency(theta, this.superInterfaces[i+ifcOffset], variable); + int idx = 0; + if (!this.firstBound.isInterface()) + bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.firstBound, variable); + for (int i = 0; i < this.superInterfaces.length; i++) + bounds[idx++] = TypeBound.createBoundOrDependency(theta, this.superInterfaces[i], variable); return bounds; }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java index 2479ee5..613555c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/TheOriginalJDTParserClass.java
@@ -2620,7 +2620,7 @@ } } } - typeDecl.createDefaultConstructor(!this.diet || insideFieldInitializer, true); + typeDecl.createDefaultConstructor(!(this.diet && this.dietInt == 0) || insideFieldInitializer, true); } } //always add <clinit> (will be remove at code gen time if empty)
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java index 8d724bc..5e551ad 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
@@ -533,7 +533,7 @@ return null; } - class AnnotationIdentityBinding { + static class AnnotationIdentityBinding { org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding internalInstance; AnnotationIdentityBinding(org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding internalInstance) { this.internalInstance = internalInstance; @@ -557,13 +557,9 @@ } } Object key = new AnnotationIdentityBinding(internalInstance); - IAnnotationBinding domInstance = - (IAnnotationBinding) this.bindingTables.compilerAnnotationBindingsToASTBindings.get(key); - if (domInstance != null) - return domInstance; - domInstance = new AnnotationBinding(internalInstance, this); - this.bindingTables.compilerAnnotationBindingsToASTBindings.put(key, domInstance); - return domInstance; + IAnnotationBinding newDomInstance = new AnnotationBinding(internalInstance, this); + IAnnotationBinding domInstance = (IAnnotationBinding) ((ConcurrentHashMap)this.bindingTables.compilerAnnotationBindingsToASTBindings).putIfAbsent(key, newDomInstance); + return domInstance != null ? domInstance : newDomInstance; } boolean isResolvedTypeInferredFromExpectedType(MethodInvocation methodInvocation) {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java index adc5e97..3a249d0 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ReorderingImportAdder.java
@@ -19,7 +19,8 @@ import java.util.Set; /** - * Totally sorts new and existing imports together, discarding the order of existing imports. + * Totally sorts new and existing imports together, discarding the order of existing imports + * and omitting duplicate entries. */ final class ReorderingImportAdder implements ImportAdder { private final Comparator<ImportName> importComparator; @@ -30,18 +31,14 @@ @Override public List<ImportName> addImports(Collection<ImportName> existingImports, Collection<ImportName> importsToAdd) { - Set<ImportName> existingImportsSet = new HashSet<ImportName>(existingImports); + int setCapacity = 2 * (existingImports.size() + importsToAdd.size()); + Set<ImportName> uniqueImportsWithAdditions = new HashSet<ImportName>(setCapacity); + uniqueImportsWithAdditions.addAll(existingImports); + uniqueImportsWithAdditions.addAll(importsToAdd); - List<ImportName> importsWithAdditions = new ArrayList<ImportName>(existingImports.size() + importsToAdd.size()); - importsWithAdditions.addAll(existingImports); - for (ImportName importToAdd : importsToAdd) { - if (!existingImportsSet.contains(importToAdd)) { - importsWithAdditions.add(importToAdd); - } - } + List<ImportName> sortedImports = new ArrayList<>(uniqueImportsWithAdditions); + Collections.sort(sortedImports, this.importComparator); - Collections.sort(importsWithAdditions, this.importComparator); - - return importsWithAdditions; + return sortedImports; } }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java index e31367b..59ba4e0 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/CommentsPreparator.java
@@ -14,10 +14,7 @@ import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE; -import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameNotAToken; -import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE; -import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRPAREN; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameWHITESPACE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamepackage; @@ -38,11 +35,13 @@ import org.eclipse.jdt.core.dom.LineComment; import org.eclipse.jdt.core.dom.MemberRef; import org.eclipse.jdt.core.dom.MethodRef; +import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.Token.WrapPolicy; public class CommentsPreparator extends ASTVisitor { @@ -54,16 +53,17 @@ private final static Pattern HTML_TAG_PATTERN; private final static Pattern HTML_ATTRIBUTE_PATTERN; static { - String formatCodeTags = "(pre)?"; //$NON-NLS-1$ - String separateLineTags = "(dl|hr|nl|p|ul|ol|table|tr)?"; //$NON-NLS-1$ - String breakBeforeTags = "(dd|dt|li|td|th|h1|h2|h3|h4|h5|h6|q)?"; //$NON-NLS-1$ - String breakAfterTags = "(br)?"; //$NON-NLS-1$ - String noFormatTags = "(code|em|tt)?"; //$NON-NLS-1$ - String otherTags = "([^<>&&\\S]+)??"; //$NON-NLS-1$ - String ws = "(?:[ \\t]+|[\\r\\n]+[ \\t]*\\*?)"; // whitespace or line break with optional asterisk //$NON-NLS-1$ - String attribute = "(?:" + ws + "+[^=&&\\S]+" + ws + "*(=)" + ws + "*\"?[^\"]*\"?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - HTML_TAG_PATTERN = Pattern.compile("<(/)?" //$NON-NLS-1$ - + formatCodeTags + separateLineTags + breakBeforeTags + breakAfterTags + noFormatTags + otherTags + String formatCodeTags = "(pre)"; //$NON-NLS-1$ + String separateLineTags = "(dl|hr|nl|p|ul|ol|table|tr)"; //$NON-NLS-1$ + String breakBeforeTags = "(dd|dt|li|td|th|h1|h2|h3|h4|h5|h6|q)"; //$NON-NLS-1$ + String breakAfterTags = "(br)"; //$NON-NLS-1$ + String noFormatTags = "(code|em|tt)"; //$NON-NLS-1$ + String otherTags = "([^<>&&\\S]++)"; //$NON-NLS-1$ + String ws = "(?>[ \\t]++|[\\r\\n]++[ \\t]*+\\*?)"; // whitespace or line break with optional asterisk //$NON-NLS-1$ + String attributeValue = "(?>\"[^\"]*\")|(?>\'[^\']*\')|[^/>\"\'&&\\S]++"; //$NON-NLS-1$ + String attribute = "(?>" + ws + "+[^=&&\\S]+" + ws + "*(=)" + ws + "*(?>" + attributeValue + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + HTML_TAG_PATTERN = Pattern.compile("<(/)?+(?:" //$NON-NLS-1$ + + formatCodeTags + '|' + separateLineTags + '|' + breakBeforeTags + '|' + breakAfterTags + '|' + noFormatTags + '|' + otherTags + ')' + "(" + attribute + "*)" + ws + "*/?>", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ HTML_ATTRIBUTE_PATTERN = Pattern.compile(attribute); } @@ -77,17 +77,12 @@ // TODO (frederic) should have another name than 'param' for the following tags // TODO (frederic) investigate how and why this list was created private final static List<String> PARAM_TAGS = Arrays.asList( - "@param", //$NON-NLS-1$ - "@exception", //$NON-NLS-1$ - "@serialField", //$NON-NLS-1$ - "@throws"); //$NON-NLS-1$ + TagElement.TAG_PARAM, + TagElement.TAG_EXCEPTION, + TagElement.TAG_SERIALFIELD, + TagElement.TAG_THROWS); - private final static List<String> IMMUTABLE_TAGS = Arrays.asList("@code", "@literal"); //$NON-NLS-1$ //$NON-NLS-2$ - - private final static int[] NO_INDENT_AFTER_COMMENT = { TokenNameRPAREN, TokenNameLBRACE, TokenNameRBRACE }; - static { - Arrays.sort(NO_INDENT_AFTER_COMMENT); - } + private final static List<String> IMMUTABLE_TAGS = Arrays.asList(TagElement.TAG_CODE, TagElement.TAG_LITERAL); private final TokenManager tm; private final DefaultCodeFormatterOptions options; @@ -172,7 +167,7 @@ if (policy == null) { int lineStart = this.tm.getPositionInLine(this.tm.findFirstTokenInLine(commentIndex - 1)); int commentStart = this.tm.getPositionInLine(commentIndex - 1); - policy = new WrapPolicy(commentStart - lineStart, commentIndex - 1, true); + policy = new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex - 1, commentStart - lineStart); } commentToken.setWrapPolicy(policy); this.lastLineComment = commentToken; @@ -187,7 +182,8 @@ if (isContinuation) { Token first = structure.get(0); first.breakBefore(); - first.setWrapPolicy(new WrapPolicy(this.lastLineCommentPosition, commentIndex - 1, false)); + first.setWrapPolicy( + new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex - 1, this.lastLineCommentPosition)); // merge previous and current line comment Token previous = this.lastLineComment; @@ -216,11 +212,13 @@ structure.get(0).clearSpaceBefore(); Token previous = this.tm.get(commentIndex - 1); + previous.clearSpaceAfter(); if (previous.originalEnd + 1 >= commentToken.originalStart) return; if (structure == null || structure.isEmpty()) { structure = new ArrayList<Token>(); structure.add(new Token(previous.originalEnd + 1, commentToken.originalEnd, TokenNameCOMMENT_LINE)); + commentToken.setInternalStructure(structure); } else { structure.add(0, new Token(previous.originalEnd + 1, commentToken.originalStart - 1, TokenNameWHITESPACE)); @@ -418,17 +416,6 @@ next = next2; } - if (previous != null && previous.getLineBreaksAfter() == 0 - && next != null && next.getLineBreaksBefore() == 0 - && Arrays.binarySearch(NO_INDENT_AFTER_COMMENT, next.tokenType) < 0) { - int policyIndent = (commentToken.getIndent() - previous.getIndent()); - WrapPolicy wrapPolicy = new WrapPolicy(policyIndent, commentIndex - 1, true); - if (this.tm.countLineBreaksBetween(previous, commentToken) == 1) - commentToken.setWrapPolicy(wrapPolicy); - if (this.tm.countLineBreaksBetween(commentToken, next) == 1) - next.setWrapPolicy(wrapPolicy); - } - if (existingBreaksBefore < existingBreaksAfter && previous != null) { commentToken.putLineBreaksAfter(previous.getLineBreaksAfter()); previous.clearLineBreaksAfter(); @@ -591,6 +578,7 @@ } Token startTokeen = this.ctm.get(startIndex); + if (startIndex > 1) startTokeen.breakBefore(); int firstTagIndex; if (this.firstTagToken == null || (firstTagIndex = this.ctm.indexOf(this.firstTagToken)) < 0 @@ -672,6 +660,11 @@ handleFormatCodeTag(startPos, endPos, isOpeningTag); } if (this.options.comment_format_html) { + if (TagElement.TAG_PARAM.equals(node.getTagName()) + && this.ctm.findIndex(startPos, -1, false) == 1 + this.ctm.firstIndexIn(node, -1)) { + continue; // it's a generic class parameter name, not an HTML tag + } + if (matcher.start(3) < matcher.end(3)) { handleSeparateLineTag(startPos, endPos); } else if (matcher.start(4) < matcher.end(4)) { @@ -697,6 +690,12 @@ return true; } + @Override + public boolean visit(QualifiedName node) { + handleReference(node); + return false; + } + private void handleReference(ASTNode node) { ASTNode parent = node.getParent(); if ((parent instanceof TagElement) && ((TagElement) parent).isNested()) { @@ -742,12 +741,12 @@ int firstPartIndex = tokenStartingAt(start); int lastPartIndex = tokenEndingAt(end); Token firstPartToken = this.ctm.get(firstPartIndex); + firstPartToken.setWrapPolicy(null); if (isOpeningTag) { firstPartToken.breakBefore(); this.ctm.get(lastPartIndex + 1).clearSpaceBefore(); } else { firstPartToken.clearSpaceBefore(); - firstPartToken.setWrapPolicy(null); } }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java index f7a3580..d971284 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
@@ -74,6 +74,7 @@ private String sourceString; private char[] sourceArray; + private IRegion[] formatRegions; private ASTNode astRoot; private List<Token> tokens = new ArrayList<Token>(); @@ -146,11 +147,12 @@ if (!regionsSatisfiesPreconditions(regions, source.length())) { throw new IllegalArgumentException(); } + this.formatRegions = regions; updateWorkingOptions(indentationLevel, lineSeparator, kind); if ((kind & K_COMMENTS_MASK) != 0) - return formatComments(source, kind & K_COMMENTS_MASK, regions); + return formatComments(source, kind & K_COMMENTS_MASK); if (prepareFormattedCode(source, kind) == null) return this.tokens.isEmpty() ? new MultiTextEdit() : null; @@ -202,15 +204,17 @@ private void findHeader() { if (this.astRoot instanceof CompilationUnit) { - List<TypeDeclaration> types = ((CompilationUnit) this.astRoot).types(); - if (!types.isEmpty()) { - int headerEndIndex = this.tokenManager.firstIndexIn(types.get(0), -1); + CompilationUnit unit = (CompilationUnit) this.astRoot; + List<TypeDeclaration> types = unit.types(); + ASTNode firstElement = types.isEmpty() ? unit.getPackage() : types.get(0); + if (firstElement != null) { + int headerEndIndex = this.tokenManager.firstIndexIn(firstElement, -1); this.tokenManager.setHeaderEndIndex(headerEndIndex); } } } - private TextEdit formatComments(String source, int kind, IRegion[] regions) { + private TextEdit formatComments(String source, int kind) { MultiTextEdit result = new MultiTextEdit(); if (!init(source)) return result; @@ -264,7 +268,8 @@ this.tokenManager.applyFormatOff(); - TextEditsBuilder resultBuilder = new TextEditsBuilder(source, regions, this.tokenManager, this.workingOptions); + TextEditsBuilder resultBuilder = new TextEditsBuilder(source, this.formatRegions, this.tokenManager, + this.workingOptions); resultBuilder.setAlignChar(DefaultCodeFormatterOptions.SPACE); for (Token token : this.tokens) { List<Token> structure = token.getInternalStructure(); @@ -282,6 +287,7 @@ ASTParser parser = ASTParser.newParser(AST.JLS8); Map<String, String> parserOptions = JavaCore.getOptions(); parserOptions.put(CompilerOptions.OPTION_Source, this.sourceLevel); + parserOptions.put(CompilerOptions.OPTION_DocCommentSupport, CompilerOptions.ENABLED); parser.setCompilerOptions(parserOptions); switch (kind & K_MASK) { @@ -370,7 +376,7 @@ private void prepareWraps(int kind) { WrapPreparator wrapPreparator = new WrapPreparator(this.tokenManager, this.workingOptions, kind); this.astRoot.accept(wrapPreparator); - wrapPreparator.finishUp(this.astRoot); + wrapPreparator.finishUp(this.astRoot, this.formatRegions); } /**
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java index f64a154..58ecff8 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatterOptions.java
@@ -710,7 +710,7 @@ options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, DefaultCodeFormatterConstants.MIXED); break; } - options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(this.tab_char == MIXED ? this.tab_size : this.indentation_size)); // reverse values swapping performed by IndentationTabPage + options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, Integer.toString(this.tab_char == SPACE ? this.indentation_size : this.tab_size)); // reverse values swapping performed by IndentationTabPage options.put(DefaultCodeFormatterConstants.FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS, this.use_tabs_only_for_leading_indentations ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE); options.put(DefaultCodeFormatterConstants.FORMATTER_WRAP_BEFORE_BINARY_OPERATOR, this.wrap_before_binary_operator ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE); options.put(DefaultCodeFormatterConstants.FORMATTER_WRAP_BEFORE_OR_OPERATOR_MULTICATCH, this.wrap_before_or_operator_multicatch ? DefaultCodeFormatterConstants.TRUE : DefaultCodeFormatterConstants.FALSE); @@ -1326,7 +1326,7 @@ // reverse values swapping performed by IndentationTabPage if (DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) this.indentation_size = indentationSize; - else + else if (JavaCore.SPACE.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) this.tab_size = indentationSize; } final Object insertNewLineAfterOpeningBraceInArrayInitializerOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_INSERT_NEW_LINE_AFTER_OPENING_BRACE_IN_ARRAY_INITIALIZER); @@ -2098,9 +2098,9 @@ // keep default } // reverse values swapping performed by IndentationTabPage - if (DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) + if (!JavaCore.SPACE.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) this.tab_size = tabSize; - else + if (!DefaultCodeFormatterConstants.MIXED.equals(settings.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) this.indentation_size = tabSize; } final Object useTabsOnlyForLeadingIndentationsOption = settings.get(DefaultCodeFormatterConstants.FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS);
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java index 32640ec..6cbfa89 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java
@@ -18,6 +18,7 @@ import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameSEMICOLON; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameelse; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamefinally; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamepackage; @@ -38,6 +39,7 @@ import org.eclipse.jdt.core.dom.BreakStatement; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ContinueStatement; import org.eclipse.jdt.core.dom.DoStatement; import org.eclipse.jdt.core.dom.EmptyStatement; import org.eclipse.jdt.core.dom.EnhancedForStatement; @@ -97,12 +99,10 @@ List<AnnotationTypeDeclaration> types = node.types(); if (!types.isEmpty()) { if (!imports.isEmpty()) - this.tm.firstTokenIn(types.get(0), -1).putLineBreaksBefore(this.options.blank_lines_after_imports + 1); - for (int i = 1; i < types.size(); i++) { - this.tm.firstTokenIn(types.get(i), -1).putLineBreaksBefore( - this.options.blank_lines_between_type_declarations + 1); + putBlankLinesBefore(types.get(0), this.options.blank_lines_after_imports); + for (int i = 1; i < types.size(); i++) + putBlankLinesBefore(types.get(i), this.options.blank_lines_between_type_declarations); } - } return true; } @@ -146,21 +146,22 @@ BodyDeclaration previous = null; for (BodyDeclaration bodyDeclaration : bodyDeclarations) { if (previous == null) { - this.tm.firstTokenIn(bodyDeclaration, -1).putLineBreaksBefore( - this.options.blank_lines_before_first_class_body_declaration + 1); + putBlankLinesBefore(bodyDeclaration, this.options.blank_lines_before_first_class_body_declaration); } else { - int blankLines; - if (bodyDeclaration instanceof FieldDeclaration) + int blankLines = 0; + if (bodyDeclaration instanceof FieldDeclaration) { blankLines = this.options.blank_lines_before_field; - else if (bodyDeclaration instanceof AbstractTypeDeclaration) + } else if (bodyDeclaration instanceof AbstractTypeDeclaration) { blankLines = this.options.blank_lines_before_member_type; - else + } else if (bodyDeclaration instanceof MethodDeclaration + || bodyDeclaration instanceof AnnotationTypeMemberDeclaration) { blankLines = this.options.blank_lines_before_method; + } if (!sameChunk(previous, bodyDeclaration)) blankLines = Math.max(blankLines, this.options.blank_lines_before_new_chunk); - this.tm.firstTokenIn(bodyDeclaration, -1).putLineBreaksBefore(blankLines + 1); + putBlankLinesBefore(bodyDeclaration, blankLines); } previous = bodyDeclaration; } @@ -171,12 +172,19 @@ return true; if (bd1 instanceof AbstractTypeDeclaration && bd2 instanceof AbstractTypeDeclaration) return true; - if ((bd1 instanceof MethodDeclaration || bd1 instanceof Initializer) - && (bd2 instanceof MethodDeclaration || bd2 instanceof Initializer)) + if ((bd1 instanceof FieldDeclaration || bd1 instanceof Initializer) + && (bd2 instanceof FieldDeclaration || bd2 instanceof Initializer)) return true; return false; } + private void putBlankLinesBefore(ASTNode node, int linesCount) { + int index = this.tm.firstIndexIn(node, -1); + while (index > 0 && this.tm.get(index - 1).tokenType == TokenNameCOMMENT_JAVADOC) + index--; + this.tm.get(index).putLineBreaksBefore(linesCount + 1); + } + @Override public boolean visit(EnumDeclaration node) { handleBracedCode(node, node.getName(), this.options.brace_position_for_enum_declaration, @@ -185,9 +193,11 @@ handleBodyDeclarations(node.bodyDeclarations()); List<EnumConstantDeclaration> enumConstants = node.enumConstants(); - for (int i = 0; i < enumConstants.size() - 1; i++) { + for (int i = 0; i < enumConstants.size(); i++) { EnumConstantDeclaration declaration = enumConstants.get(i); - if (declaration.getAnonymousClassDeclaration() != null) + if (declaration.getJavadoc() != null) + this.tm.firstTokenIn(declaration, TokenNameCOMMENT_JAVADOC).breakBefore(); + if (declaration.getAnonymousClassDeclaration() != null && i < enumConstants.size() - 1) this.tm.firstTokenAfter(declaration, TokenNameCOMMA).breakAfter(); } @@ -239,11 +249,16 @@ @Override public boolean visit(MethodDeclaration node) { + this.declarationModifierVisited = false; + + if (node.getBody() == null) + return true; + if (node.isConstructor()) { handleBracedCode(node.getBody(), null, this.options.brace_position_for_constructor_declaration, this.options.indent_statements_compare_to_body, this.options.insert_new_line_in_empty_method_body); - } else if (node.getBody() != null) { + } else { handleBracedCode(node.getBody(), null, this.options.brace_position_for_method_declaration, this.options.indent_statements_compare_to_body, this.options.insert_new_line_in_empty_method_body); @@ -251,7 +266,6 @@ if (openBrace.getLineBreaksAfter() > 0) // if not, these are empty braces openBrace.putLineBreaksAfter(this.options.blank_lines_at_beginning_of_method_body + 1); } - this.declarationModifierVisited = false; return true; } @@ -297,6 +311,10 @@ if (this.options.indent_switchstatements_compare_to_cases) { int nonBreakStatementEnd = -1; for (Statement statement : statements) { + boolean isBreaking = statement instanceof BreakStatement || statement instanceof ReturnStatement + || statement instanceof ContinueStatement || statement instanceof Block; + if (isBreaking) + addEmptyLineTokenAfter(this.tm.lastIndexIn(statement, -1)); if (statement instanceof SwitchCase) { if (nonBreakStatementEnd >= 0) { // indent only comments between previous and current statement @@ -306,8 +324,7 @@ } else if (!(statement instanceof BreakStatement || statement instanceof Block)) { indent(statement); } - nonBreakStatementEnd = (statement instanceof BreakStatement || statement instanceof ReturnStatement) - ? -1 : this.tm.lastIndexIn(statement, -1); + nonBreakStatementEnd = isBreaking ? -1 : this.tm.lastIndexIn(statement, -1); } if (nonBreakStatementEnd >= 0) { // indent comments between last statement and closing brace @@ -354,28 +371,34 @@ @Override public boolean visit(ArrayInitializer node) { int openBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE); - Token afterOpenBraceToken = this.tm.get(openBraceIndex + 1); - boolean isEmpty = afterOpenBraceToken.tokenType == TokenNameRBRACE; - if (isEmpty && this.options.keep_empty_array_initializer_on_one_line) - return true; + int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); + + boolean isEmpty = openBraceIndex + 1 == closeBraceIndex; + if (isEmpty) { + addEmptyLineTokenAfter(openBraceIndex); + closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); + } Token openBraceToken = this.tm.get(openBraceIndex); - int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); + Token closeBraceToken = this.tm.get(closeBraceIndex); + + if (!(node.getParent() instanceof ArrayInitializer)) { + Token afterOpenBraceToken = this.tm.get(openBraceIndex + 1); + for (int i = 0; i < this.options.continuation_indentation_for_array_initializer; i++) { + afterOpenBraceToken.indent(); + closeBraceToken.unindent(); + } + } + + if (!isEmpty || !this.options.keep_empty_array_initializer_on_one_line) handleBracePosition(openBraceToken, closeBraceIndex, this.options.brace_position_for_array_initializer); if (!isEmpty) { - Token closeBraceToken = this.tm.get(closeBraceIndex); if (this.options.insert_new_line_after_opening_brace_in_array_initializer) openBraceToken.breakAfter(); if (this.options.insert_new_line_before_closing_brace_in_array_initializer) closeBraceToken.breakBefore(); - if (!(node.getParent() instanceof ArrayInitializer)) { - for (int i = 0; i < this.options.continuation_indentation_for_array_initializer; i++) { - afterOpenBraceToken.indent(); - closeBraceToken.unindent(); } - } - } return true; } @@ -449,9 +472,11 @@ breakAfter = this.options.insert_new_line_after_annotation_on_type; } else if (parentNode instanceof FieldDeclaration) { breakAfter = this.options.insert_new_line_after_annotation_on_field; - } else if (parentNode instanceof MethodDeclaration - || parentNode instanceof AnnotationTypeMemberDeclaration) { + } else if (parentNode instanceof MethodDeclaration) { breakAfter = this.options.insert_new_line_after_annotation_on_method; + } else if (parentNode instanceof AnnotationTypeMemberDeclaration) { + breakAfter = this.options.insert_new_line_after_annotation_on_method + && ((AnnotationTypeMemberDeclaration) parentNode).getDefault() != node; } else if (parentNode instanceof VariableDeclarationStatement || parentNode instanceof VariableDeclarationExpression) { breakAfter = this.options.insert_new_line_after_annotation_on_local_variable; @@ -483,12 +508,15 @@ } private void handleLoopBody(Statement body) { - if (!(body instanceof Block) - && !(body instanceof EmptyStatement && !this.options.put_empty_statement_on_new_line)) { + if (body instanceof Block) + return; + if (body instanceof EmptyStatement && !this.options.put_empty_statement_on_new_line + && !(body.getParent() instanceof IfStatement)) + return; breakLineBefore(body); + addEmptyLineTokenAfter(this.tm.lastIndexIn(body, -1)); indent(body); } - } @Override public boolean visit(IfStatement node) { @@ -498,21 +526,16 @@ if (this.options.insert_new_line_before_else_in_if_statement || !(thenNode instanceof Block)) this.tm.firstTokenBefore(elseNode, TokenNameelse).breakBefore(); - boolean keepElseOnSameLine = (elseNode instanceof Block) - || (this.options.keep_else_statement_on_same_line) + boolean keepElseOnSameLine = (this.options.keep_else_statement_on_same_line) || (this.options.compact_else_if && (elseNode instanceof IfStatement)); - if (!keepElseOnSameLine) { - breakLineBefore(elseNode); - indent(elseNode); + if (!keepElseOnSameLine) + handleLoopBody(elseNode); } - } boolean keepThenOnSameLine = this.options.keep_then_statement_on_same_line || (this.options.keep_simple_if_on_one_line && elseNode == null); - if (!keepThenOnSameLine && !(thenNode instanceof Block)) { - breakLineBefore(thenNode); - indent(thenNode); - } + if (!keepThenOnSameLine) + handleLoopBody(thenNode); return true; } @@ -543,6 +566,7 @@ : this.tm.firstIndexAfter(nodeBeforeOpenBrace, TokenNameLBRACE); int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); Token openBraceToken = this.tm.get(openBraceIndex); + Token closeBraceToken = this.tm.get(closeBraceIndex); handleBracePosition(openBraceToken, closeBraceIndex, bracePosition); boolean isEmpty = true; @@ -552,13 +576,16 @@ break; } } + + addEmptyLineTokenAfter(openBraceIndex); + if (!isEmpty || newLineInEmpty) { openBraceToken.breakAfter(); - this.tm.get(closeBraceIndex).breakBefore(); + closeBraceToken.breakBefore(); } if (indentBody) { this.tm.get(openBraceIndex + 1).indent(); - this.tm.get(closeBraceIndex).unindent(); + closeBraceToken.unindent(); } } @@ -575,6 +602,32 @@ } } + private void addEmptyLineTokenAfter(int tokenIndex) { + if (tokenIndex + 1 >= this.tm.size()) + return; + Token token = this.tm.get(tokenIndex); + Token next = this.tm.get(tokenIndex + 1); + if (this.tm.countLineBreaksBetween(token, next) < 2 || !this.options.indent_empty_lines) + return; + + // find a line break and make a token out of it + for (int i = token.originalEnd + 1; i < next.originalStart; i++) { + char c = this.tm.charAt(i); + char c2 = this.tm.charAt(i + 1); + int lineBreakStart = (c == '\r' || c == '\n') ? i : -1; + int lineBreakEnd = ((c2 == '\r' || c2 == '\n') && c2 != c) ? i + 1 : lineBreakStart; + if (lineBreakStart >= 0) { + Token emptyLineToken = new Token(lineBreakStart, lineBreakEnd, Token.TokenNameEMPTY_LINE); + emptyLineToken.breakBefore(); + emptyLineToken.breakAfter(); + emptyLineToken.setToEscape(true); // force text builder to use toString() + this.tm.insert(tokenIndex + 1, emptyLineToken); + return; + } + } + assert false; + } + private void indent(ASTNode node) { int startIndex = this.tm.firstIndexIn(node, -1); while (startIndex > 0 && this.tm.get(startIndex - 1).isComment())
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java index 63a49a4..bfc18dc 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/SpacePreparator.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 Mateusz Matela and others. + * Copyright (c) 2014, 2016 Mateusz Matela 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 @@ -46,6 +46,7 @@ import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IfStatement; +import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.InstanceofExpression; import org.eclipse.jdt.core.dom.IntersectionType; @@ -56,6 +57,7 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.PostfixExpression; @@ -102,6 +104,18 @@ } @Override + public boolean visit(PackageDeclaration node) { + handleSemicolon(node); + return true; + } + + @Override + public boolean visit(ImportDeclaration node) { + handleSemicolon(node); + return true; + } + + @Override public boolean visit(TypeDeclaration node) { if (node.getName().getStartPosition() == -1) return true; // this is a fake type created by parsing in class body mode @@ -111,6 +125,11 @@ List<TypeParameter> typeParameters = node.typeParameters(); handleTypeParameters(typeParameters); + if (!node.isInterface() && !node.superInterfaceTypes().isEmpty()) { + // fix for: class A<E> extends ArrayList<String>implements Callable<String> + handleToken(node.getName(), TokenNameimplements, true, false); + } + handleToken(node.getName(), TokenNameLBRACE, this.options.insert_space_before_opening_brace_in_type_declaration, false); handleCommas(node.superInterfaceTypes(), this.options.insert_space_before_comma_in_superinterfaces, @@ -122,6 +141,8 @@ public boolean visit(EnumDeclaration node) { handleToken(node.getName(), TokenNameLBRACE, this.options.insert_space_before_opening_brace_in_enum_declaration, false); + handleCommas(node.superInterfaceTypes(), this.options.insert_space_before_comma_in_superinterfaces, + this.options.insert_space_after_comma_in_superinterfaces); handleCommas(node.enumConstants(), this.options.insert_space_before_comma_in_enum_declarations, this.options.insert_space_after_comma_in_enum_declarations); return true; @@ -186,9 +207,14 @@ } else { handleToken(node.getName(), TokenNameLPAREN, spaceBeforeOpenParen, spaceAfterOpenParen); - if (node.isConstructor() ? this.options.insert_space_before_closing_paren_in_constructor_declaration - : this.options.insert_space_before_closing_paren_in_method_declaration) - handleToken(node.getName(), TokenNameRPAREN, true, false); + boolean spaceBeforeCloseParen = node.isConstructor() + ? this.options.insert_space_before_closing_paren_in_constructor_declaration + : this.options.insert_space_before_closing_paren_in_method_declaration; + if (spaceBeforeCloseParen) { + List<SingleVariableDeclaration> params = node.parameters(); + ASTNode beforeBrace = params.isEmpty() ? node.getName() : params.get(params.size() - 1); + handleTokenAfter(beforeBrace, TokenNameRPAREN, true, false); + } } if ((node.isConstructor() ? this.options.insert_space_before_opening_brace_in_constructor_declaration @@ -222,6 +248,8 @@ handleTokenBefore(typeParameters.get(0), TokenNameLESS, true, false); handleTokenAfter(typeParameters.get(typeParameters.size() - 1), TokenNameGREATER, false, true); } + + handleSemicolon(node); return true; } @@ -243,6 +271,7 @@ handleToken((ASTNode) node.fragments().get(0), TokenNameIdentifier, true, false); handleCommas(node.fragments(), this.options.insert_space_before_comma_in_multiple_field_declarations, this.options.insert_space_after_comma_in_multiple_field_declarations); + handleSemicolon(node); return true; } @@ -287,6 +316,7 @@ this.options.insert_space_before_closing_paren_in_switch, false); handleTokenAfter(node.getExpression(), TokenNameLBRACE, this.options.insert_space_before_opening_brace_in_switch, false); + handleSemicolon(node.statements()); return true; } @@ -369,6 +399,7 @@ @Override public boolean visit(AssertStatement node) { + this.tm.firstTokenIn(node, TokenNameassert).spaceAfter(); if (node.getMessage() != null) { handleTokenBefore(node.getMessage(), TokenNameCOLON, this.options.insert_space_before_colon_in_assert, this.options.insert_space_after_colon_in_assert); @@ -416,6 +447,7 @@ @Override public boolean visit(AnnotationTypeMemberDeclaration node) { + handleToken(node.getName(), TokenNameIdentifier, true, false); handleToken(node.getName(), TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_annotation_type_member_declaration, false); handleEmptyParens(node.getName(), @@ -457,11 +489,16 @@ if (handleParenthesis) { handleToken(node, TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_annotation, this.options.insert_space_after_opening_paren_in_annotation); - handleToken(node, TokenNameRPAREN, this.options.insert_space_before_closing_paren_in_annotation, false); + if (this.options.insert_space_before_closing_paren_in_annotation) + this.tm.lastTokenIn(node, TokenNameRPAREN).spaceBefore(); } ASTNode parent = node.getParent(); - if (!(parent instanceof Annotation) && !(parent instanceof ArrayInitializer)) + boolean skipSpaceAfter = parent instanceof Annotation || parent instanceof MemberValuePair + || (parent instanceof AnnotationTypeMemberDeclaration + && ((AnnotationTypeMemberDeclaration) parent).getDefault() == node) + || parent instanceof ArrayInitializer; + if (!skipSpaceAfter) this.tm.lastTokenIn(node, -1).spaceAfter(); } @@ -479,7 +516,7 @@ this.options.insert_space_before_opening_paren_in_method_declaration, this.options.insert_space_after_opening_paren_in_method_declaration); - handleToken(node, TokenNameRPAREN, + handleTokenBefore(node.getBody(), TokenNameRPAREN, this.options.insert_space_before_closing_paren_in_method_declaration, false); } handleCommas(parameters, this.options.insert_space_before_comma_in_method_declaration_parameters, @@ -490,20 +527,20 @@ @Override public boolean visit(Block node) { - if (node.getParent().getLength() == 0) + handleSemicolon(node.statements()); + + ASTNode parent = node.getParent(); + if (parent.getLength() == 0) return true; // this is a fake block created by parsing in statements mode - if (node.getParent() instanceof MethodDeclaration) + if (parent instanceof MethodDeclaration) return true; // spaces handled in #visit(MethodDeclaration) handleToken(node, TokenNameLBRACE, this.options.insert_space_before_opening_brace_in_block, false); - if (this.options.insert_space_after_closing_brace_in_block) { + if (this.options.insert_space_after_closing_brace_in_block + && (parent instanceof Statement || parent instanceof CatchClause)) { int closeBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); - if (closeBraceIndex + 1 < this.tm.size()) { - int nextToken = this.tm.get(closeBraceIndex + 1).tokenType; - if (nextToken != TokenNameSEMICOLON && nextToken != TokenNameRPAREN) this.tm.get(closeBraceIndex).spaceAfter(); } - } return true; } @@ -511,14 +548,19 @@ public boolean visit(IfStatement node) { handleToken(node, TokenNameLPAREN, this.options.insert_space_before_opening_paren_in_if, this.options.insert_space_after_opening_paren_in_if); - handleTokenBefore(node.getThenStatement(), TokenNameRPAREN, - this.options.insert_space_before_closing_paren_in_if, true); Statement thenStatement = node.getThenStatement(); - if (thenStatement instanceof Block && this.tm.isGuardClause((Block) node.getThenStatement())) { + int closingParenIndex = this.tm.firstIndexBefore(thenStatement, TokenNameRPAREN); + handleToken(this.tm.get(closingParenIndex), this.options.insert_space_before_closing_paren_in_if, + /* space before then statement may be needed if it will stay on the same line */ + !(thenStatement instanceof Block) && !this.tm.get(closingParenIndex + 1).isComment()); + + if (thenStatement instanceof Block && this.tm.isGuardClause((Block) thenStatement)) { handleToken(thenStatement, TokenNameLBRACE, false, true); this.tm.lastTokenIn(node, TokenNameRBRACE).spaceBefore(); } + + handleSemicolon(thenStatement); return true; } @@ -588,7 +630,7 @@ @Override public boolean visit(ClassInstanceCreation node) { - handleInvocation(node, node.getType(), node.typeArguments()); + handleInvocation(node, node.getType(), node.typeArguments(), node.getAnonymousClassDeclaration()); handleCommas(node.arguments(), this.options.insert_space_before_comma_in_allocation_expression, this.options.insert_space_after_comma_in_allocation_expression); return true; @@ -613,6 +655,11 @@ } private void handleInvocation(ASTNode invocationNode, ASTNode nodeBeforeOpeningParen, List<Type> typeArguments) { + handleInvocation(invocationNode, nodeBeforeOpeningParen, typeArguments, null); + } + + private void handleInvocation(ASTNode invocationNode, ASTNode nodeBeforeOpeningParen, List<Type> typeArguments, + ASTNode nodeAfterClosingParen) { if (handleEmptyParens(nodeBeforeOpeningParen, this.options.insert_space_between_empty_parens_in_method_invocation)) { handleToken(nodeBeforeOpeningParen, TokenNameLPAREN, @@ -622,7 +669,10 @@ this.options.insert_space_before_opening_paren_in_method_invocation, this.options.insert_space_after_opening_paren_in_method_invocation); if (this.options.insert_space_before_closing_paren_in_method_invocation) { - this.tm.lastTokenIn(invocationNode, TokenNameRPAREN).spaceBefore(); + Token closingParen = nodeAfterClosingParen == null + ? this.tm.lastTokenIn(invocationNode, TokenNameRPAREN) + : this.tm.firstTokenBefore(nodeAfterClosingParen, TokenNameRPAREN); + closingParen.spaceBefore(); } } @@ -808,14 +858,11 @@ @Override public boolean visit(ParameterizedType node) { + List<Type> typeArguments = node.typeArguments(); + if (!typeArguments.isEmpty()) { handleTokenAfter(node.getType(), TokenNameLESS, this.options.insert_space_before_opening_angle_bracket_in_parameterized_type_reference, this.options.insert_space_after_opening_angle_bracket_in_parameterized_type_reference); - List<Type> typeArguments = node.typeArguments(); - if (typeArguments.isEmpty()) { - handleTokenAfter(node.getType(), TokenNameGREATER, - this.options.insert_space_before_closing_angle_bracket_in_parameterized_type_reference, false); - } else { handleTokenAfter(typeArguments.get(typeArguments.size() - 1), TokenNameGREATER, this.options.insert_space_before_closing_angle_bracket_in_parameterized_type_reference, false); handleCommas(node.typeArguments(), @@ -914,6 +961,7 @@ private void handleToken(ASTNode node, int tokenType, boolean spaceBefore, boolean spaceAfter) { if (spaceBefore || spaceAfter) { Token token = this.tm.get(this.tm.findIndex(node.getStartPosition(), tokenType, true)); + // ^not the same as "firstTokenIn(node, tokenType)" - do not assert the token is inside the node handleToken(token, spaceBefore, spaceAfter); } } @@ -973,6 +1021,21 @@ return false; } + private void handleSemicolon(ASTNode node) { + if (this.options.insert_space_before_semicolon) { + Token lastToken = this.tm.lastTokenIn(node, -1); + if (lastToken.tokenType == TokenNameSEMICOLON) + lastToken.spaceBefore(); + } + } + + private void handleSemicolon(List<ASTNode> nodes) { + if (this.options.insert_space_before_semicolon) { + for (ASTNode node : nodes) + handleSemicolon(node); + } + } + public void finishUp() { this.tm.traverse(0, new TokenTraverser() { boolean isPreviousJIDP = false;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java index f1b92e1..37bbe6e 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TextEditsBuilder.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 Mateusz Matela and others. + * Copyright (c) 2014, 2016 Mateusz Matela 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 @@ -22,6 +22,7 @@ import java.util.List; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.Token.WrapPolicy; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; @@ -152,8 +153,16 @@ this.stringLiteralsInLine.clear(); if (getLineBreaksBefore() > 1) { Token indentToken = null; - if (this.options.indent_empty_lines && token.tokenType != TokenNameNotAToken) - indentToken = token.getIndent() > getPrevious().getIndent() ? token : getPrevious(); + if (this.options.indent_empty_lines && token.tokenType != TokenNameNotAToken) { + if (index == 0) { + indentToken = token; + } else { + boolean isForced = token.getWrapPolicy() != null + && token.getWrapPolicy().wrapMode == WrapMode.FORCED; + Token previous = this.tm.get(this.tm.findFirstTokenInLine(index - 1, true, !isForced)); + indentToken = (token.getIndent() > previous.getIndent()) ? token : previous; + } + } for (int i = 1; i < getLineBreaksBefore(); i++) { bufferLineSeparator(token, true); if (indentToken != null) @@ -222,15 +231,24 @@ int wrapRootIndent = indent; if (index == -1) { // this means we print a line separator in a multi-line comment TokenManager tm2 = this.parent.tm; - wrapRootIndent = tm2.get(tm2.findFirstTokenInLine(this.parentTokenIndex, true)).getIndent(); + wrapRootIndent = tm2.get(tm2.findFirstTokenInLine(this.parentTokenIndex, true, true)).getIndent(); } else if (wrapPolicy != null) { - wrapRootIndent = this.tm.get(this.tm.findFirstTokenInLine(index, true)).getIndent(); + wrapRootIndent = this.tm.get(this.tm.findFirstTokenInLine(index, true, true)).getIndent(); } additionalSpaces = indent - wrapRootIndent; indent = wrapRootIndent; - if (wrapPolicy != null && wrapPolicy.isForced) { - int extraIndent = wrapPolicy.extraIndent; + if (wrapPolicy != null && wrapPolicy.wrapMode == WrapMode.FORCED) { + int parentIndex = wrapPolicy.wrapParentIndex; + int parentAlign = 0; + int lineStart = this.tm.findFirstTokenInLine(parentIndex); + for (int i = parentIndex; i >= lineStart; i--) { + parentAlign = this.tm.get(i).getAlign(); + if (parentAlign > 0) + break; + } + + int extraIndent = parentAlign == 0 ? wrapPolicy.extraIndent : (token.getIndent() - parentAlign); additionalSpaces -= extraIndent; indent += extraIndent; } @@ -316,29 +334,43 @@ String buffered = this.buffer.toString(); boolean sourceMatch = this.source.startsWith(buffered, this.counter) && this.counter + buffered.length() == currentPosition; - if (!sourceMatch && checkRegions(this.counter, currentPosition)) { - TextEdit edit = getReplaceEdit(this.counter, currentPosition, buffered); - this.edits.add(edit); + while (!sourceMatch && this.currentRegion < this.regions.size()) { + IRegion region = this.regions.get(this.currentRegion); + if (currentPosition < region.getOffset()) + break; + int regionEnd = region.getOffset() + region.getLength(); + if (this.counter >= regionEnd) { + this.currentRegion++; + continue; + } + if (this.currentRegion == this.regions.size() - 1 + || this.regions.get(this.currentRegion + 1).getOffset() > currentPosition) { + this.edits.add(getReplaceEdit(this.counter, currentPosition, buffered, region)); + break; + } + + // this edit will span more than one region, split it + IRegion nextRegion = this.regions.get(this.currentRegion + 1); + int bestSplit = 0; + int bestSplitScore = Integer.MAX_VALUE; + for (int i = 0; i < buffered.length(); i++) { + ReplaceEdit edit1 = getReplaceEdit(this.counter, regionEnd, buffered.substring(0, i), region); + ReplaceEdit edit2 = getReplaceEdit(regionEnd, currentPosition, buffered.substring(i), nextRegion); + int score = edit1.getLength() + edit1.getText().length() + edit2.getLength() + edit2.getText().length(); + if (score < bestSplitScore) { + bestSplit = i; + bestSplitScore = score; + } + } + this.edits.add(getReplaceEdit(this.counter, regionEnd, buffered.substring(0, bestSplit), region)); + buffered = buffered.substring(bestSplit); + this.counter = regionEnd; } this.buffer.setLength(0); this.counter = currentPosition; } - private boolean checkRegions(int editStart, int editEnd) { - while (true) { - if (this.currentRegion >= this.regions.size()) - return false; - IRegion region = this.regions.get(this.currentRegion); - if (editEnd < region.getOffset()) - return false; - if (editStart < region.getOffset() + region.getLength()) - return true; - this.currentRegion++; - } - } - - private TextEdit getReplaceEdit(int editStart, int editEnd, String text) { - IRegion region = this.regions.get(this.currentRegion); + private ReplaceEdit getReplaceEdit(int editStart, int editEnd, String text, IRegion region) { int regionEnd = region.getOffset() + region.getLength(); if (editStart < region.getOffset() && regionEnd < editEnd) { int breaksInReplacement = this.tm.countLineBreaksBetween(text, 0, text.length());
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java index 4e6ad4f..ffd5e02 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/Token.java
@@ -7,6 +7,7 @@ * * Contributors: * Mateusz Matela <mateusz.matela@gmail.com> - [formatter] Formatter does not format Java code correctly, especially when max line width is set - https://bugs.eclipse.org/303519 + * Till Brychcy - Java Code Formatter breaks code if single line comments contain unicode escape - https://bugs.eclipse.org/471090 *******************************************************************************/ package org.eclipse.jdt.internal.formatter; @@ -24,50 +25,64 @@ */ public class Token { + public static enum WrapMode { + /** + * Wrap mode for the "Do not wrap" policy. Tokens still should be indented as if wrapped when a preceding line + * break cannot be removed due to a line comment or formatting region restriction. + */ + DISABLED, + /** Wrap mode for the "Wrap where necessary" policies. */ + WHERE_NECESSARY, + /** Wrap mode for the "Wrap all elements" policies. */ + TOP_PRIORITY, + /** + * Wrap mode for tokens that are already in new line before wrapping, but their indentation should be adjusted + * in similar way to wrapping. Used for anonymous class body, lambda body and comments inside code. + */ + FORCED + } + public static class WrapPolicy { /** Policy used for internal structure of multiline comments to mark tokens that should never be wrapped */ - public final static WrapPolicy DISABLE_WRAP = new WrapPolicy(0, 0, false); + public final static WrapPolicy DISABLE_WRAP = new WrapPolicy(WrapMode.DISABLED, 0, 0); /** * Policy used for internal structure of multiline comments to mark tokens that can be wrapped only in lines * that have no other tokens to wrap. */ - public final static WrapPolicy SUBSTITUTE_ONLY = new WrapPolicy(0, 0, false); + public final static WrapPolicy SUBSTITUTE_ONLY = new WrapPolicy(WrapMode.DISABLED, 0, 0); - public final int extraIndent; + public final WrapMode wrapMode; public final int wrapParentIndex; + public final int groupEndIndex; + public final int extraIndent; public final int structureDepth; public final float penaltyMultiplier; public final boolean isFirstInGroup; public final boolean indentOnColumn; - public final int topPriorityGroupEnd; - /** - * If true, it means the token was in new line even before wrapping, but should be treaded as wrapped token for - * indentation purposes. Used for anonymous class body, lambda body and comments inside code. - */ - public final boolean isForced; - public WrapPolicy(int extraIndent, int wrapParentIndex, int structureDepth, float penaltyMultiplier, - boolean isFirstInGroup, boolean indentOnColumn, int topPriorityGroupEnd, boolean isForced) { - this.extraIndent = extraIndent; + public WrapPolicy(WrapMode wrapMode, int wrapParentIndex, int groupEndIndex, int extraIndent, + int structureDepth, float penaltyMultiplier, boolean isFirstInGroup, boolean indentOnColumn) { + assert wrapMode != null && (wrapParentIndex < groupEndIndex || groupEndIndex == -1); + + this.wrapMode = wrapMode; this.wrapParentIndex = wrapParentIndex; + this.groupEndIndex = groupEndIndex; + this.extraIndent = extraIndent; this.structureDepth = structureDepth; this.penaltyMultiplier = penaltyMultiplier; this.isFirstInGroup = isFirstInGroup; this.indentOnColumn = indentOnColumn; - this.topPriorityGroupEnd = topPriorityGroupEnd; - this.isForced = isForced; } - public WrapPolicy(int extraIndent, int wrapParentIndex, boolean isForced) { - this(extraIndent, wrapParentIndex, 0, 1, false, false, -1, isForced); + public WrapPolicy(WrapMode wrapMode, int wrapParentIndex, int extraIndent) { + this(wrapMode, wrapParentIndex, -1, extraIndent, 0, 1, false, false); + } } - public boolean isTopPriority() { - return this.topPriorityGroupEnd >= 0; - } - } + /** Special token type used to mark tokens that store empty line indentation */ + public static final int TokenNameEMPTY_LINE = 10000; /** Position in source of the first character. */ public final int originalStart; @@ -119,9 +134,8 @@ int end = scanner.getCurrentTokenEndPosition(); if (currentToken == TokenNameCOMMENT_LINE) { // don't include line separator - String source = scanner.getCurrentTokenString(); - for (int i = source.length() - 1; i > 0; i--) { - char c = source.charAt(i); + while(end >= start) { + char c = scanner.source[end]; if (c != '\r' && c != '\n') break; end--; @@ -242,7 +256,8 @@ } public boolean isWrappable() { - return this.wrapPolicy != null && !this.wrapPolicy.isForced; + WrapPolicy wp = this.wrapPolicy; + return wp != null && wp.wrapMode != WrapMode.DISABLED && wp.wrapMode != WrapMode.FORCED; } public void setNLSTag(Token nlsTagToken) { @@ -276,6 +291,8 @@ } public String toString(String source) { + if (this.tokenType == TokenNameEMPTY_LINE) + return ""; //$NON-NLS-1$ return source.substring(this.originalStart, this.originalEnd + 1); }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java index 054d8b2..8d18424 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 Mateusz Matela and others. + * Copyright (c) 2014, 2016 Mateusz Matela 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 @@ -10,7 +10,12 @@ *******************************************************************************/ package org.eclipse.jdt.internal.formatter; -import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.*; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameLBRACE; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameNotAToken; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameWHITESPACE; import java.util.ArrayList; import java.util.HashMap; @@ -24,6 +29,7 @@ import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.ThrowStatement; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.linewrap.CommentWrapExecutor; /** @@ -379,16 +385,19 @@ } public int findFirstTokenInLine(int startIndex) { - return findFirstTokenInLine(startIndex, false); + return findFirstTokenInLine(startIndex, false, false); } - public int findFirstTokenInLine(int startIndex, boolean includeWraps) { + public int findFirstTokenInLine(int startIndex, boolean includeWraps, boolean includeForced) { Token previous = get(startIndex); // going backwards, previous has higher index than current for (int i = startIndex - 1; i >= 0; i--) { Token token = get(i); - int lineBreaks = Math.max(token.getLineBreaksAfter(), previous.getLineBreaksBefore()); - if (lineBreaks > 0 && (!includeWraps || previous.getWrapPolicy() == null)) + if (token.getLineBreaksAfter() > 0 || previous.getLineBreaksBefore() > 0) { + boolean include = previous.getWrapPolicy() != null + && (previous.getWrapPolicy().wrapMode == WrapMode.FORCED ? includeForced : includeWraps); + if (!include) return i + 1; + } previous = token; } return 0; @@ -430,8 +439,10 @@ if (this.formatOffTagPairs == null) return; for (Token[] pair : this.formatOffTagPairs) { - int index1 = indexOf(pair[0]); - int index2 = indexOf(pair[1]); + int index1 = findIndex(pair[0].originalStart, -1, false); + int index2 = findIndex(pair[1].originalEnd, -1, false); + pair[0] = get(index1); + pair[1] = get(index2); Token unformatted = new Token(pair[0].originalStart, pair[1].originalEnd, TokenNameWHITESPACE); unformatted.setIndent(Math.min(pair[0].getIndent(), findSourcePositionInLine(pair[0].originalStart))); unformatted.putLineBreaksBefore(pair[0].getLineBreaksBefore());
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java index 9eb02b7..05fab8d 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/CommentWrapExecutor.java
@@ -23,6 +23,7 @@ import org.eclipse.jdt.internal.formatter.Token; import org.eclipse.jdt.internal.formatter.TokenManager; import org.eclipse.jdt.internal.formatter.TokenTraverser; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.Token.WrapPolicy; public class CommentWrapExecutor extends TokenTraverser { @@ -225,16 +226,18 @@ if (prefix.tokenType == TokenNameWHITESPACE) { whitespace = new Token(prefix); whitespace.breakBefore(); - whitespace.setWrapPolicy(new WrapPolicy(0, commentIndex, false)); + whitespace.setIndent(indent); + whitespace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex, 0)); prefix = structure.get(1); + assert prefix.tokenType == TokenNameCOMMENT_LINE; } int prefixEnd = commentToken.originalStart + 1; - if (prefix.tokenType == TokenNameCOMMENT_LINE) - prefixEnd = Math.max(prefixEnd, prefix.originalEnd); + if (!prefix.hasNLSTag()) + prefixEnd = Math.max(prefixEnd, prefix.originalEnd); // comments can start with more than 2 slashes prefix = new Token(commentToken.originalStart, prefixEnd, TokenNameCOMMENT_LINE); if (whitespace == null) { prefix.breakBefore(); - prefix.setWrapPolicy(new WrapPolicy(0, commentIndex, false)); + prefix.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commentIndex, 0)); } int lineStartIndex = whitespace == null ? 0 : 1;
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java index 8321a34..916751f 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/FieldAligner.java
@@ -21,9 +21,7 @@ import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.SimpleName; -import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions; import org.eclipse.jdt.internal.formatter.Token; import org.eclipse.jdt.internal.formatter.TokenManager; import org.eclipse.jdt.internal.formatter.TokenTraverser; @@ -62,56 +60,27 @@ } } - private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<List<FieldDeclaration>>(); + private final List<List<FieldDeclaration>> fieldAlignGroups = new ArrayList<>(); final TokenManager tm; - private final DefaultCodeFormatterOptions options; - - public FieldAligner(TokenManager tokenManager, DefaultCodeFormatterOptions options) { + public FieldAligner(TokenManager tokenManager) { this.tm = tokenManager; - this.options = options; } - public void prepareAlign(TypeDeclaration node) { - List<FieldDeclaration> bodyDeclarations = node.bodyDeclarations(); - ArrayList<FieldDeclaration> alignGroup = new ArrayList<FieldDeclaration>(); - BodyDeclaration previous = null; + public void prepareAlign(List<FieldDeclaration> bodyDeclarations) { + ArrayList<FieldDeclaration> alignGroup = new ArrayList<>(); for (BodyDeclaration declaration : bodyDeclarations) { - if (!alignGroup.isEmpty()) { - if ((declaration instanceof FieldDeclaration) && !areSeparated(previous, declaration)) { + if ((declaration instanceof FieldDeclaration)) { alignGroup.add((FieldDeclaration) declaration); } else { alignFields(alignGroup); - alignGroup = new ArrayList<FieldDeclaration>(); - } + alignGroup = new ArrayList<>(); } - if (alignGroup.isEmpty()) { - if (declaration instanceof FieldDeclaration) - alignGroup.add((FieldDeclaration) declaration); } - previous = declaration; - } alignFields(alignGroup); } - private boolean areSeparated(BodyDeclaration declaration1, BodyDeclaration declaration2) { - // check if there are more empty lines between fields than normal - if (this.options.number_of_empty_lines_to_preserve <= this.options.blank_lines_before_field) - return false; - int maxLineBreaks = 0; - int from = this.tm.lastIndexIn(declaration1, -1); - int to = this.tm.firstIndexIn(declaration2, -1); - - Token previous = this.tm.get(from); - for (int i = from + 1; i <= to; i++) { - Token token = this.tm.get(i); - maxLineBreaks = Math.max(maxLineBreaks, this.tm.countLineBreaksBetween(previous, token)); - previous = token; - } - return maxLineBreaks - 1 > this.options.blank_lines_before_field; - } - private void alignFields(ArrayList<FieldDeclaration> alignGroup) { if (alignGroup.size() < 2) return; @@ -125,7 +94,7 @@ int positionInLine = this.tm.getPositionInLine(nameIndex); maxNameAlign = Math.max(maxNameAlign, positionInLine); } - maxNameAlign = this.tm.toIndent(maxNameAlign, true); + maxNameAlign = this.tm.toIndent(maxNameAlign, false); int maxAssignAlign = 0; for (FieldDeclaration declaration : alignGroup) { @@ -142,7 +111,7 @@ maxAssignAlign = Math.max(maxAssignAlign, positionInLine); } } - maxAssignAlign = this.tm.toIndent(maxAssignAlign, true); + maxAssignAlign = this.tm.toIndent(maxAssignAlign, false); for (FieldDeclaration declaration : alignGroup) { List<VariableDeclarationFragment> fragments = declaration.fragments(); @@ -151,16 +120,9 @@ int assingIndex = this.tm.firstIndexAfter(fragment.getName(), TokenNameEQUAL); Token assignToken = this.tm.get(assingIndex); assignToken.setAlign(maxAssignAlign); - - int baseIndent = this.tm.getPositionInLine(assingIndex + 1) - assignToken.getIndent(); - int lastIndex = this.tm.lastIndexIn(declaration, -1); - for (int i = assingIndex + 1; i <= lastIndex; i++) { - Token token = this.tm.get(i); - token.setIndent(baseIndent + token.getIndent()); } } } - } public void alignComments() { if (this.fieldAlignGroups.isEmpty()) @@ -176,7 +138,7 @@ maxCommentAlign = Math.max(maxCommentAlign, positionCounter.findMaxPosition(firstIndexInLine, lastIndex)); } - maxCommentAlign = this.tm.toIndent(maxCommentAlign, true); + maxCommentAlign = this.tm.toIndent(maxCommentAlign, false); for (FieldDeclaration declaration : alignGroup) { int typeIndex = this.tm.firstIndexIn(declaration.getType(), -1);
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java index 791c385..5f906dc 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java
@@ -26,6 +26,7 @@ import org.eclipse.jdt.internal.formatter.Token; import org.eclipse.jdt.internal.formatter.TokenManager; import org.eclipse.jdt.internal.formatter.TokenTraverser; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.Token.WrapPolicy; public class WrapExecutor { @@ -74,8 +75,6 @@ public static final WrapResult NO_WRAP_NEEDED = new WrapResult(0, 0, null); - public static final WrapResult TOP_PRIORITY_WRAP_MET = new WrapResult(0, 0, null); - public final double penalty; public final int totalExtraLines; /** @@ -91,6 +90,17 @@ } } + private static class WrapRestartThrowable extends Throwable { + private static final long serialVersionUID = -2980600077230803443L; // backward compatible + + public final int topPriorityWrap; + + public WrapRestartThrowable(int topPriorityWrap) { + super(null, null, false, false); + this.topPriorityWrap = topPriorityWrap; + } + } + private class LineAnalyzer extends TokenTraverser { final private CommentWrapExecutor commentWrapper; @@ -133,10 +143,10 @@ if (token.isWrappable()) { WrapPolicy wrapPolicy = token.getWrapPolicy(); - if (wrapPolicy.isTopPriority() && getLineBreaksBefore() == 0 + if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY && getLineBreaksBefore() == 0 && index > this.currentTopPriorityGroupEnd) { this.topPriorityGroupStarts.add(index); - this.currentTopPriorityGroupEnd = wrapPolicy.topPriorityGroupEnd; + this.currentTopPriorityGroupEnd = wrapPolicy.groupEndIndex; } if (this.firstPotentialWrap < 0 && getWrapIndent(token) < this.counter) this.firstPotentialWrap = index; @@ -274,8 +284,6 @@ final TokenManager tm; final DefaultCodeFormatterOptions options; - private int topPriorityWrapIndex; - private final WrapInfo wrapInfoTemp = new WrapInfo(); public WrapExecutor(TokenManager tokenManager, DefaultCodeFormatterOptions options) { @@ -286,86 +294,102 @@ public void executeWraps() { int index = 0; - mainLoop: while (index < this.tm.size()) { + while (index < this.tm.size()) { Token token = this.tm.get(index); - handleOnColumnIndent(index, token.getWrapPolicy()); - // this might be a pre-existing wrap that should trigger other top priority wraps - int jumpToIndex = handleTopPriorityWraps(index); - if (jumpToIndex >= 0) { - index = jumpToIndex; - continue mainLoop; + while (true) { + try { + int currentIndent = getWrapIndent(token); + this.wrapSearchResults.clear(); + index = applyWraps(index, currentIndent); + break; + } catch (WrapRestartThrowable e) { + handleTopPriorityWraps(e); + } } - - // determine wraps for incoming line - int currentIndent = getWrapIndent(token); - boolean isLineWrapped = token.isWrappable(); this.wrapSearchResults.clear(); - WrapResult wrapResult = findWraps(index, currentIndent); - if (wrapResult == WrapResult.TOP_PRIORITY_WRAP_MET) { - jumpToIndex = handleTopPriorityWraps(this.topPriorityWrapIndex); - assert jumpToIndex >= 0; - index = Math.min(index, jumpToIndex); - continue mainLoop; + this.usedTopPriorityWraps.clear(); } - // apply wraps and indents - WrapInfo wrapInfo = wrapResult.nextWrap; - while (wrapInfo != null) { - isLineWrapped = true; - for (; index < wrapInfo.wrapTokenIndex; index++) { - token = this.tm.get(index); - if (shouldForceWrap(token, currentIndent)) { - currentIndent = token.getIndent(); - wrapInfo = new WrapInfo(index, currentIndent); - findWrapsCached(index, currentIndent); - break; - } - token.setIndent(currentIndent); - } - token = this.tm.get(index); - token.breakBefore(); - token.setIndent(currentIndent = wrapInfo.indent); - handleOnColumnIndent(index, token.getWrapPolicy()); - jumpToIndex = handleTopPriorityWraps(index); - if (jumpToIndex >= 0) { - index = jumpToIndex; - continue mainLoop; - } - wrapInfo = this.wrapSearchResults.get(wrapInfo).nextWrap; - } - - // apply indent until the beginning of the next line - token.setIndent(currentIndent); - for (index++; index < this.tm.size(); index++) { - if (token.getLineBreaksAfter() > 0) - break; - token = this.tm.get(index); - if (token.isNextLineOnWrap() && isLineWrapped) - token.breakBefore(); - if (token.getLineBreaksBefore() > 0) - break; - if (shouldForceWrap(token, currentIndent)) - currentIndent = token.getIndent(); - token.setIndent(currentIndent); - } - } - this.wrapSearchResults.clear(); - this.usedTopPriorityWraps.clear(); - this.tm.traverse(0, new NLSTagHandler()); + } + + private int applyWraps(int index, int indent) throws WrapRestartThrowable { + WrapInfo wrapInfo = findWrapsCached(index, indent).nextWrap; + Token token = this.tm.get(index); + index++; + token.setIndent(indent); + int groupEnd = token.getWrapPolicy() != null ? token.getWrapPolicy().groupEndIndex : -1; + while (index < this.tm.size()) { + token = this.tm.get(index); + if (token.isNextLineOnWrap() && this.tm.get(this.tm.findFirstTokenInLine(index)).isWrappable()) { + token.breakBefore(); + return index; + } + while (wrapInfo != null && wrapInfo.wrapTokenIndex < index) + wrapInfo = this.wrapSearchResults.get(wrapInfo).nextWrap; + if (wrapInfo != null && wrapInfo.wrapTokenIndex == index) { + token.breakBefore(); + handleOnColumnIndent(index, token.getWrapPolicy()); + checkTopPriorityWraps(index); + index = applyWraps(index, wrapInfo.indent); + continue; + } + + boolean isNewLine = this.tm.get(index - 1).getLineBreaksAfter() > 0 || token.getLineBreaksBefore() > 0; + if (isNewLine) { + if (token.getWrapPolicy() != null) { + handleOnColumnIndent(index, token.getWrapPolicy()); + checkTopPriorityWraps(index); + int newIndent = getWrapIndent(token); + if (newIndent < indent) + return index; + wrapInfo = findWrapsCached(index, newIndent).nextWrap; + if (newIndent > indent) { + index = applyWraps(index, newIndent); + continue; + } + } else if (index > groupEnd) { + return index; + } + } else { + checkForceWrap(token, index, indent); + } + + token.setIndent(indent); + index++; + } + return index; } - private WrapResult findWrapsCached(int startTokenIndex, int indent) { + private WrapResult findWrapsCached(int startTokenIndex, int indent) throws WrapRestartThrowable { this.wrapInfoTemp.wrapTokenIndex = startTokenIndex; this.wrapInfoTemp.indent = indent; WrapResult wrapResult = this.wrapSearchResults.get(this.wrapInfoTemp); + if (wrapResult == null && this.wrapSearchResults.containsKey(this.wrapInfoTemp)) + return null; // no wrap needed + + // pre-existing result may be based on different wrapping of earlier tokens and therefore be wrong + WrapResult wr = wrapResult; + while (wr != null && wr.nextWrap != null) { + WrapInfo wi = wr.nextWrap; + Token token = this.tm.get(wi.wrapTokenIndex); + if (token.getWrapPolicy().wrapParentIndex < startTokenIndex && getWrapIndent(token) != wi.indent) { + wrapResult = null; + break; + } + wr = this.wrapSearchResults.get(wi); + } + if (wrapResult == null) { Token token = this.tm.get(startTokenIndex); boolean wasLineBreak = token.getLineBreaksBefore() > 0; token.breakBefore(); + try { wrapResult = findWraps(startTokenIndex, indent); + } finally { if (!wasLineBreak) token.clearLineBreaksBefore(); + } WrapInfo wrapInfo = new WrapInfo(startTokenIndex, indent); this.wrapSearchResults.put(wrapInfo, wrapResult); @@ -377,7 +401,7 @@ * The main algorithm that looks for optimal places to wrap. * Calls itself recursively to get results for wrapped sub-lines. */ - private WrapResult findWraps(int wrapTokenIndex, int indent) { + private WrapResult findWraps(int wrapTokenIndex, int indent) throws WrapRestartThrowable { final int lastIndex = this.lineAnalyzer.analyzeLine(wrapTokenIndex, indent); final boolean lineExceeded = this.lineAnalyzer.lineExceeded; final int lastPosition = this.lineAnalyzer.getLastPosition(); @@ -390,7 +414,7 @@ final int[] topPriorityGroupStarts = toArray(this.lineAnalyzer.topPriorityGroupStarts); int topPriorityIndex = topPriorityGroupStarts.length - 1; int nearestGroupEnd = topPriorityIndex == -1 ? 0 - : this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().topPriorityGroupEnd; + : this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().groupEndIndex; double bestTotalPenalty = getWrapPenalty(wrapTokenIndex, indent, lastIndex + 1, -1, WrapResult.NO_WRAP_NEEDED); int bestExtraLines = lineExceeded ? Integer.MAX_VALUE : extraLines; // if line is exceeded, accept every wrap @@ -402,7 +426,8 @@ if ((!lineExceeded || firstPotentialWrap < 0) && lastIndex + 1 < this.tm.size()) { Token nextLineToken = this.tm.get(lastIndex + 1); - if (nextLineToken.isWrappable() && (this.tm.get(lastIndex).isComment() || nextLineToken.isComment())) { + if ((nextLineToken.getWrapPolicy() != null && nextLineToken.getWrapPolicy().wrapMode != WrapMode.FORCED) + && (this.tm.get(lastIndex).isComment() || nextLineToken.isComment())) { // this might be a pre-existing wrap forced by a comment, calculate penalties as normal bestIndent = getWrapIndent(nextLineToken); bestNextWrap = lastIndex + 1; @@ -414,8 +439,7 @@ if (firstPotentialWrap < 0 && lineExceeded) { if (topPriorityGroupStarts.length > 0) { - this.topPriorityWrapIndex = topPriorityGroupStarts[0]; - return WrapResult.TOP_PRIORITY_WRAP_MET; + checkTopPriorityWraps(topPriorityGroupStarts[0]); } // Report high number of extra lines to encourage the algorithm to look @@ -439,7 +463,7 @@ assert i == topPriorityGroupStarts[topPriorityIndex]; topPriorityIndex--; nearestGroupEnd = topPriorityIndex == -1 ? 0 - : this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().topPriorityGroupEnd; + : this.tm.get(topPriorityGroupStarts[topPriorityIndex]).getWrapPolicy().groupEndIndex; } if (!token.isWrappable()) @@ -448,9 +472,6 @@ int nextWrapIndent = getWrapIndent(token); WrapResult nextWrapResult = findWrapsCached(i, nextWrapIndent); - if (nextWrapResult == WrapResult.TOP_PRIORITY_WRAP_MET) - continue; - double totalPenalty = getWrapPenalty(wrapTokenIndex, indent, i, nextWrapIndent, nextWrapResult); int totalExtraLines = extraLines + nextWrapResult.totalExtraLines; boolean isBetter = totalExtraLines < bestExtraLines || bestExtraLines == Integer.MAX_VALUE; @@ -468,8 +489,7 @@ } if (bestNextWrap == -1 && lineExceeded && topPriorityGroupStarts.length > 0) { - this.topPriorityWrapIndex = topPriorityGroupStarts[0]; - return WrapResult.TOP_PRIORITY_WRAP_MET; + checkTopPriorityWraps(topPriorityGroupStarts[0]); } return new WrapResult(bestTotalPenalty, bestExtraLines, @@ -477,7 +497,7 @@ } private double getWrapPenalty(int lineStartIndex, int lineIndent, int wrapIndex, int wrapIndent, - WrapResult wrapResult) { + WrapResult wrapResult) throws WrapRestartThrowable { WrapPolicy wrapPolicy = null; Token wrapToken = null; if (wrapIndex < this.tm.size()) { @@ -521,12 +541,12 @@ // a wrap of the same parent (bar2). If so, then bar1 must be wrapped (so give it negative penalty). // Update: Actually, every token that is followed by a higher level depth wrap should be also wrapped, // as long as this next wrap is not the last in line and the token is not the first in its wrap group. - WrapResult nextWrapResult = wrapResult; + WrapInfo nextWrap = wrapResult.nextWrap; boolean checkDepth = wrapToken != null && wrapToken.isWrappable() && (lineStartWrapPolicy == null || wrapPolicy.structureDepth >= lineStartWrapPolicy.structureDepth); double penaltyDiff = 0; - while (checkDepth && nextWrapResult.nextWrap != null) { - WrapPolicy nextPolicy = this.tm.get(nextWrapResult.nextWrap.wrapTokenIndex).getWrapPolicy(); + while (checkDepth && nextWrap != null) { + WrapPolicy nextPolicy = this.tm.get(nextWrap.wrapTokenIndex).getWrapPolicy(); if (nextPolicy.wrapParentIndex == wrapPolicy.wrapParentIndex || (penaltyDiff != 0 && !wrapPolicy.isFirstInGroup)) { penalty -= penaltyDiff * 1.25; @@ -535,7 +555,7 @@ if (nextPolicy.structureDepth <= wrapPolicy.structureDepth) break; penaltyDiff = Math.max(penaltyDiff, getPenalty(nextPolicy)); - nextWrapResult = this.wrapSearchResults.get(nextWrapResult.nextWrap); + nextWrap = findWrapsCached(nextWrap.wrapTokenIndex, nextWrap.indent).nextWrap; } return penalty + wrapResult.penalty; @@ -545,40 +565,40 @@ return Math.exp(policy.structureDepth) * policy.penaltyMultiplier; } - private boolean shouldForceWrap(Token token, int currentIndent) { + private void checkForceWrap(Token token, int index, int currentIndent) throws WrapRestartThrowable { // A token that will have smaller indent when wrapped than the current line indent, // should be wrapped because it's a low depth token following some complex wraps of higher depth. // This rule could not be implemented in getWrapPenalty() because a token's wrap indent may depend // on wraps in previous lines, which are not determined yet when the token's penalty is calculated. - if (token.isWrappable() && this.options.wrap_outer_expressions_when_nested) { - int indent = getWrapIndent(token); - if (indent < currentIndent) { + if (token.isWrappable() && this.options.wrap_outer_expressions_when_nested + && getWrapIndent(token) < currentIndent) { + WrapPolicy lineStartPolicy = this.tm.get(this.tm.findFirstTokenInLine(index, false, true)).getWrapPolicy(); + if (lineStartPolicy != null && lineStartPolicy.wrapMode != WrapMode.FORCED) { token.breakBefore(); - token.setIndent(indent); - return true; + throw new WrapRestartThrowable(-1); + } } } - return false; + + private void checkTopPriorityWraps(int wrapIndex) throws WrapRestartThrowable { + WrapPolicy wrapPolicy = this.tm.get(wrapIndex).getWrapPolicy(); + if (wrapPolicy != null && wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY + && !this.usedTopPriorityWraps.contains(wrapPolicy)) + throw new WrapRestartThrowable(wrapIndex); } - /** - * @return index of the first token in the top priority group that given token belongs to or -1 if it doesn't belong - * to any top priority. - */ - private int handleTopPriorityWraps(int wrapIndex) { - // wrap all tokens in the same top priority group and jump back to the first one + private void handleTopPriorityWraps(WrapRestartThrowable restartException) { + int wrapIndex = restartException.topPriorityWrap; + if (wrapIndex < 0) + return; WrapPolicy wrapPolicy = this.tm.get(wrapIndex).getWrapPolicy(); - if (wrapPolicy == null || !wrapPolicy.isTopPriority() || this.usedTopPriorityWraps.contains(wrapPolicy)) - return -1; - int firstTokenIndex = -1; int parentIndex = wrapPolicy.wrapParentIndex; for (int i = wrapIndex; i > parentIndex; i--) { Token token = this.tm.get(i); wrapPolicy = token.getWrapPolicy(); if (wrapPolicy != null && wrapPolicy.wrapParentIndex == parentIndex) { - if (wrapPolicy.isTopPriority()) { + if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY) { token.breakBefore(); - firstTokenIndex = i; this.usedTopPriorityWraps.add(wrapPolicy); } if (wrapPolicy.isFirstInGroup) @@ -594,14 +614,13 @@ } else if (wrapPolicy != null && wrapPolicy.wrapParentIndex == parentIndex) { if (wrapPolicy.isFirstInGroup) break; - if (wrapPolicy.isTopPriority()) { + if (wrapPolicy.wrapMode == WrapMode.TOP_PRIORITY) { token.breakBefore(); this.usedTopPriorityWraps.add(wrapPolicy); } } breakAfterPrevious = token.getLineBreaksAfter() > 0; } - return firstTokenIndex; } private int[] toArray(List<Integer> list) { @@ -634,8 +653,8 @@ int getWrapIndent(Token token) { WrapPolicy policy = token.getWrapPolicy(); - if (policy == null || (token.getLineBreaksBefore() > 1 && !policy.isForced && !policy.isTopPriority())) - return token.getIndent(); // no additional indentation after an empty line + if (policy == null) + return token.getIndent(); if (this.options.never_indent_line_comments_on_first_column && token.tokenType == TokenNameCOMMENT_LINE && token.getIndent() == 0)
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java index b973b6f..d4f54d5 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 Mateusz Matela and others. + * Copyright (c) 2014, 2016 Mateusz Matela 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 @@ -12,6 +12,7 @@ package org.eclipse.jdt.internal.formatter.linewrap; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOLON; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMA; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_BLOCK; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_JAVADOC; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameCOMMENT_LINE; @@ -23,11 +24,14 @@ import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameQUESTION; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRBRACE; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameRPAREN; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameSEMICOLON; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameextends; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameimplements; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameIdentifier; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamenew; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamesuper; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethis; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNamethrows; import java.util.ArrayList; @@ -38,6 +42,7 @@ import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.Assignment; @@ -49,6 +54,7 @@ import org.eclipse.jdt.core.dom.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.ImportDeclaration; @@ -58,10 +64,13 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SuperFieldAccess; import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -74,12 +83,54 @@ import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions; import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions.Alignment; import org.eclipse.jdt.internal.formatter.Token; +import org.eclipse.jdt.internal.formatter.Token.WrapMode; import org.eclipse.jdt.internal.formatter.Token.WrapPolicy; import org.eclipse.jdt.internal.formatter.TokenManager; import org.eclipse.jdt.internal.formatter.TokenTraverser; +import org.eclipse.jface.text.IRegion; public class WrapPreparator extends ASTVisitor { + /** + * Helper for common handling of all expressions that should be treated the same as {@link FieldAccess} + */ + private static class FieldAccessAdapter { + final Expression accessExpression; + + public FieldAccessAdapter(Expression expression) { + this.accessExpression = expression; + } + + public static boolean isFieldAccess(ASTNode expr) { + return expr instanceof FieldAccess || expr instanceof QualifiedName || expr instanceof ThisExpression + || expr instanceof SuperFieldAccess; + } + + public Expression getExpression() { + if (this.accessExpression instanceof FieldAccess) + return ((FieldAccess) this.accessExpression).getExpression(); + if (this.accessExpression instanceof QualifiedName) + return ((QualifiedName) this.accessExpression).getQualifier(); + if (this.accessExpression instanceof ThisExpression) + return ((ThisExpression) this.accessExpression).getQualifier(); + if (this.accessExpression instanceof SuperFieldAccess) + return ((SuperFieldAccess) this.accessExpression).getQualifier(); + throw new AssertionError(); + } + + public int getIdentifierIndex(TokenManager tm) { + if (this.accessExpression instanceof FieldAccess) + return tm.firstIndexIn(((FieldAccess) this.accessExpression).getName(), TokenNameIdentifier); + if (this.accessExpression instanceof QualifiedName) + return tm.firstIndexIn(((QualifiedName) this.accessExpression).getName(), TokenNameIdentifier); + if (this.accessExpression instanceof ThisExpression) + return tm.lastIndexIn(this.accessExpression, TokenNamethis); + if (this.accessExpression instanceof SuperFieldAccess) + return tm.lastIndexIn(this.accessExpression, TokenNamesuper); + throw new AssertionError(); + } + } + private final static Map<Operator, Integer> OPERATOR_PRECEDENCE; static { HashMap<Operator, Integer> precedence = new HashMap<Operator, Integer>(); @@ -106,7 +157,7 @@ final DefaultCodeFormatterOptions options; final int kind; - FieldAligner fieldAligner; + final FieldAligner fieldAligner; int importsStart = -1, importsEnd = -1; @@ -115,6 +166,8 @@ * parameters */ private List<Integer> wrapIndexes = new ArrayList<Integer>(); + /** Indexes for wraps that shouldn't happen but should be indented if cannot be removed */ + private List<Integer> secondaryWrapIndexes = new ArrayList<Integer>(); private List<Float> wrapPenalties = new ArrayList<Float>(); private int wrapParentIndex = -1; private int wrapGroupEnd = -1; @@ -125,11 +178,17 @@ this.tm = tokenManager; this.options = options; this.kind = kind; + + this.fieldAligner = new FieldAligner(this.tm); } @Override public boolean preVisit2(ASTNode node) { this.currentDepth++; + + assert this.wrapIndexes.isEmpty() && this.secondaryWrapIndexes.isEmpty() && this.wrapPenalties.isEmpty(); + assert this.wrapParentIndex == -1 && this.wrapGroupEnd == -1; + boolean isMalformed = (node.getFlags() & ASTNode.MALFORMED) != 0; if (isMalformed) { this.tm.addDisableFormatTokenPair(this.tm.firstTokenIn(node, -1), this.tm.lastTokenIn(node, -1)); @@ -173,19 +232,28 @@ if (!superInterfaceTypes.isEmpty()) { int implementsToken = node.isInterface() ? TokenNameextends : TokenNameimplements; this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); - this.wrapGroupEnd = this.tm.lastIndexIn(superInterfaceTypes.get(superInterfaceTypes.size() - 1), -1); this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), implementsToken)); - for (Type type : superInterfaceTypes) - this.wrapIndexes.add(this.tm.firstIndexIn(type, -1)); + prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1); handleWrap(this.options.alignment_for_superinterfaces_in_type_declaration, PREFERRED); } - if (this.options.align_type_members_on_columns) { - if (this.fieldAligner == null) { - this.fieldAligner = new FieldAligner(this.tm, this.options); + if (this.options.align_type_members_on_columns) + this.fieldAligner.prepareAlign(node.bodyDeclarations()); + + return true; } - this.fieldAligner.prepareAlign(node); + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + if (this.options.align_type_members_on_columns) + this.fieldAligner.prepareAlign(node.bodyDeclarations()); + return true; } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + if (this.options.align_type_members_on_columns) + this.fieldAligner.prepareAlign(node.bodyDeclarations()); return true; } @@ -205,26 +273,28 @@ List<Type> exceptionTypes = node.thrownExceptionTypes(); if (!exceptionTypes.isEmpty()) { - this.wrapParentIndex = this.tm.firstIndexBefore(exceptionTypes.get(0), TokenNameRPAREN); - this.wrapGroupEnd = this.tm.lastIndexIn(exceptionTypes.get(exceptionTypes.size() - 1), -1); int wrappingOption = node.isConstructor() ? this.options.alignment_for_throws_clause_in_constructor_declaration : this.options.alignment_for_throws_clause_in_method_declaration; - for (Type exceptionType : exceptionTypes) - this.wrapIndexes.add(this.tm.firstIndexIn(exceptionType, -1)); + if ((wrappingOption & Alignment.M_INDENT_ON_COLUMN) == 0) + this.wrapParentIndex = this.tm.firstIndexAfter(node.getName(), TokenNameLPAREN); + prepareElementsList(exceptionTypes, TokenNameCOMMA, TokenNameRPAREN); // instead of the first exception type, wrap the "throws" token this.wrapIndexes.set(0, this.tm.firstIndexBefore(exceptionTypes.get(0), TokenNamethrows)); handleWrap(wrappingOption, 0.5f); } if (!node.isConstructor()) { + this.wrapParentIndex = this.tm.findFirstTokenInLine(this.tm.firstIndexIn(node.getName(), -1)); List<TypeParameter> typeParameters = node.typeParameters(); if (!typeParameters.isEmpty()) this.wrapIndexes.add(this.tm.firstIndexIn(typeParameters.get(0), -1)); - if (node.getReturnType2() != null) - this.wrapIndexes.add(this.tm.firstIndexIn(node.getReturnType2(), -1)); + if (node.getReturnType2() != null) { + int returTypeIndex = this.tm.firstIndexIn(node.getReturnType2(), -1); + if (returTypeIndex != this.wrapParentIndex) + this.wrapIndexes.add(returTypeIndex); + } this.wrapIndexes.add(this.tm.firstIndexIn(node.getName(), -1)); - this.wrapParentIndex = this.tm.findFirstTokenInLine(this.tm.firstIndexIn(node.getName(), -1)); this.wrapGroupEnd = this.tm.lastIndexIn(node.getName(), -1); handleWrap(this.options.alignment_for_method_declaration); } @@ -234,24 +304,47 @@ @Override public boolean visit(EnumDeclaration node) { List<EnumConstantDeclaration> enumConstants = node.enumConstants(); + int constantsEnd = -1; if (!enumConstants.isEmpty()) { for (EnumConstantDeclaration constant : enumConstants) this.wrapIndexes.add(this.tm.firstIndexIn(constant, -1)); - this.wrapParentIndex = this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE); - this.wrapGroupEnd = this.tm.lastIndexIn(enumConstants.get(enumConstants.size() - 1), -1); + this.wrapParentIndex = (this.options.alignment_for_enum_constants & Alignment.M_INDENT_ON_COLUMN) > 0 + ? this.tm.firstIndexBefore(enumConstants.get(0), TokenNameLBRACE) : this.tm.firstIndexIn(node, -1); + this.wrapGroupEnd = constantsEnd = this.tm.lastIndexIn(enumConstants.get(enumConstants.size() - 1), -1); handleWrap(this.options.alignment_for_enum_constants, node); } + if (!this.options.join_wrapped_lines) { + // preserve a line break between the last comma and semicolon + int commaIndex = -1; + int i = constantsEnd > 0 ? constantsEnd : this.tm.firstIndexAfter(node.getName(), TokenNameLBRACE); + while (++i < this.tm.size()) { + Token t = this.tm.get(i); + if (t.isComment()) + continue; + if (t.tokenType == TokenNameCOMMA) { + commaIndex = i; + continue; + } + if (t.tokenType == TokenNameSEMICOLON && commaIndex >= 0 + && this.tm.countLineBreaksBetween(this.tm.get(commaIndex), t) == 1) { + t.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, commaIndex, 0)); + } + break; + } + } + List<Type> superInterfaceTypes = node.superInterfaceTypes(); if (!superInterfaceTypes.isEmpty()) { - this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements)); - for (Type type : superInterfaceTypes) - this.wrapIndexes.add(this.tm.firstIndexIn(type, -1)); this.wrapParentIndex = this.tm.lastIndexIn(node.getName(), -1); - this.wrapGroupEnd = this.tm.lastIndexIn(superInterfaceTypes.get(superInterfaceTypes.size() - 1), -1); - this.wrapPenalties.add(PREFERRED); - handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, node); + this.wrapIndexes.add(this.tm.firstIndexBefore(superInterfaceTypes.get(0), TokenNameimplements)); + prepareElementsList(superInterfaceTypes, TokenNameCOMMA, -1); + handleWrap(this.options.alignment_for_superinterfaces_in_enum_declaration, PREFERRED); } + + if (this.options.align_type_members_on_columns) + this.fieldAligner.prepareAlign(node.bodyDeclarations()); + return true; } @@ -277,8 +370,10 @@ while (expression instanceof MethodInvocation) { invocation = (MethodInvocation) expression; expression = invocation.getExpression(); - if (expression != null) + if (expression != null) { this.wrapIndexes.add(this.tm.firstIndexBefore(invocation.getName(), TokenNameDOT)); + this.secondaryWrapIndexes.add(this.tm.firstIndexIn(invocation.getName(), TokenNameIdentifier)); + } } Collections.reverse(this.wrapIndexes); this.wrapParentIndex = (expression != null) ? this.tm.lastIndexIn(expression, -1) @@ -322,6 +417,62 @@ } @Override + public boolean visit(FieldAccess node) { + handleFieldAccess(node); + return true; + } + + @Override + public boolean visit(QualifiedName node) { + handleFieldAccess(node); + return true; + } + + @Override + public boolean visit(ThisExpression node) { + handleFieldAccess(node); + return true; + } + + @Override + public boolean visit(SuperFieldAccess node) { + handleFieldAccess(node); + return true; + } + + private void handleFieldAccess(Expression node) { + boolean isAccessChainRoot = !FieldAccessAdapter.isFieldAccess(node.getParent()); + if (!isAccessChainRoot) + return; + + Expression expression = node; + FieldAccessAdapter access = null; + while (FieldAccessAdapter.isFieldAccess(expression)) { + access = new FieldAccessAdapter(expression); + int nameIndex = access.getIdentifierIndex(this.tm); + // find a dot preceding the name, may not be there + for (int i = nameIndex - 1; i > this.tm.firstIndexIn(node, -1); i--) { + Token t = this.tm.get(i); + if (t.tokenType == TokenNameDOT) { + this.wrapIndexes.add(i); + this.secondaryWrapIndexes.add(nameIndex); + } + if (!t.isComment() && t.tokenType != TokenNamesuper) + break; + } + expression = access.getExpression(); + } + Collections.reverse(this.wrapIndexes); + this.wrapParentIndex = this.tm.lastIndexIn(expression != null ? expression : access.accessExpression, -1); + boolean isFollowedByInvocation = node.getParent() instanceof MethodInvocation + && node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY; + this.wrapGroupEnd = isFollowedByInvocation ? this.tm.lastIndexIn(node.getParent(), -1) + : new FieldAccessAdapter(node).getIdentifierIndex(this.tm); + // TODO need configuration for this, now only handles line breaks that cannot be removed + handleWrap(Alignment.M_NO_ALIGNMENT); + } + + @Override public boolean visit(InfixExpression node) { Integer operatorPrecedence = OPERATOR_PRECEDENCE.get(node.getOperator()); if (operatorPrecedence == null) @@ -333,7 +484,8 @@ findTokensToWrap(node, 0); this.wrapParentIndex = this.wrapIndexes.remove(0); this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); - if ((this.options.alignment_for_binary_expression & Alignment.M_INDENT_ON_COLUMN) != 0) + if ((this.options.alignment_for_binary_expression & Alignment.M_INDENT_ON_COLUMN) != 0 + && this.wrapParentIndex > 0) this.wrapParentIndex--; for (int i = this.wrapParentIndex; i >= 0; i--) { if (!this.tm.get(i).isComment()) { @@ -367,6 +519,7 @@ assert node.getOperator().toString().equals(this.tm.toString(indexBefore)); int indexAfter = this.tm.firstIndexIn(operand, -1); this.wrapIndexes.add(this.options.wrap_before_binary_operator ? indexBefore : indexAfter); + this.secondaryWrapIndexes.add(this.options.wrap_before_binary_operator ? indexAfter : indexBefore); if (!this.options.join_wrapped_lines) { // TODO there should be an option for never joining wraps on opposite side of the operator @@ -393,6 +546,8 @@ public boolean visit(ConditionalExpression node) { this.wrapIndexes.add(this.tm.firstIndexAfter(node.getExpression(), TokenNameQUESTION)); this.wrapIndexes.add(this.tm.firstIndexAfter(node.getThenExpression(), TokenNameCOLON)); + this.secondaryWrapIndexes.add(this.tm.firstIndexIn(node.getThenExpression(), -1)); + this.secondaryWrapIndexes.add(this.tm.firstIndexIn(node.getElseExpression(), -1)); this.wrapParentIndex = this.tm.lastIndexIn(node.getExpression(), -1); this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); handleWrap(this.options.alignment_for_conditional_expression); @@ -403,10 +558,7 @@ public boolean visit(ArrayInitializer node) { List<Expression> expressions = node.expressions(); if (!expressions.isEmpty()) { - for (Expression expression : expressions) - this.wrapIndexes.add(this.tm.firstIndexIn(expression, -1)); - this.wrapParentIndex = this.tm.firstIndexBefore(expressions.get(0), TokenNameLBRACE); - this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); + prepareElementsList(expressions, TokenNameCOMMA, TokenNameLBRACE); handleWrap(this.options.alignment_for_expressions_in_array_initializer, node); } if (!this.options.join_wrapped_lines @@ -416,8 +568,8 @@ Token closingBrace = this.tm.get(closingBraceIndex); if (this.tm.countLineBreaksBetween(this.tm.get(closingBraceIndex - 1), closingBrace) == 1) { int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE); - closingBrace.setWrapPolicy( - new WrapPolicy(0, openingBraceIndex, this.currentDepth, 1, true, false, -1, false)); + closingBrace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingBraceIndex, + closingBraceIndex, 0, this.currentDepth, 1, true, false)); } } return true; @@ -425,14 +577,18 @@ @Override public boolean visit(Assignment node) { - this.wrapIndexes.add(this.tm.firstIndexIn(node.getRightHandSide(), -1)); + int wrapIndex = this.tm.firstIndexIn(node.getRightHandSide(), -1); + if (this.tm.get(wrapIndex).getLineBreaksBefore() > 0) + return true; int operatorIndex = this.tm.firstIndexBefore(node.getRightHandSide(), -1); while (this.tm.get(operatorIndex).isComment()) operatorIndex--; assert node.getOperator().toString().equals(this.tm.toString(operatorIndex)); - this.wrapParentIndex = operatorIndex; + this.wrapIndexes.add(wrapIndex); + this.secondaryWrapIndexes.add(operatorIndex); + this.wrapParentIndex = operatorIndex - 1; this.wrapGroupEnd = this.tm.lastIndexIn(node.getRightHandSide(), -1); handleWrap(this.options.alignment_for_assignment); return true; @@ -440,12 +596,18 @@ @Override public boolean visit(VariableDeclarationFragment node) { - if (node.getInitializer() != null) { - this.wrapIndexes.add(this.tm.firstIndexIn(node.getInitializer(), -1)); - this.wrapParentIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL); + if (node.getInitializer() == null) + return true; + int wrapIndex = this.tm.firstIndexIn(node.getInitializer(), -1); + if (this.tm.get(wrapIndex).getLineBreaksBefore() > 0) + return true; + int equalIndex = this.tm.firstIndexBefore(node.getInitializer(), TokenNameEQUAL); + + this.wrapIndexes.add(wrapIndex); + this.secondaryWrapIndexes.add(equalIndex); + this.wrapParentIndex = equalIndex - 1; this.wrapGroupEnd = this.tm.lastIndexIn(node.getInitializer(), -1); handleWrap(this.options.alignment_for_assignment); - } return true; } @@ -453,45 +615,55 @@ public boolean visit(IfStatement node) { if (!(node.getThenStatement() instanceof Block)) { int thenIndex = this.tm.firstIndexIn(node.getThenStatement(), -1); - if (this.tm.get(thenIndex).getLineBreaksBefore() == 0) + if (this.tm.get(thenIndex).getLineBreaksBefore() == 0) { this.wrapIndexes.add(thenIndex); + this.wrapParentIndex = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); + this.wrapGroupEnd = this.tm.lastIndexIn(node.getThenStatement(), -1); + handleWrap(this.options.alignment_for_compact_if, node); + } } Statement elseStatement = node.getElseStatement(); - if (elseStatement != null && !(elseStatement instanceof Block)) { + if (elseStatement != null && !(elseStatement instanceof Block) && !(elseStatement instanceof IfStatement)) { int elseIndex = this.tm.firstIndexIn(elseStatement, -1); - if (this.tm.get(elseIndex).getLineBreaksBefore() == 0) + if (this.tm.get(elseIndex).getLineBreaksBefore() == 0) { this.wrapIndexes.add(elseIndex); - } - if (!this.wrapIndexes.isEmpty()) { this.wrapParentIndex = this.tm.firstIndexAfter(node.getExpression(), TokenNameRPAREN); - this.wrapGroupEnd = this.tm.lastIndexIn(node, -1); + this.wrapGroupEnd = this.tm.lastIndexIn(elseStatement, -1); handleWrap(this.options.alignment_for_compact_if, node); } + } return true; } @Override public boolean visit(TryStatement node) { - handleArguments(node.resources(), this.options.alignment_for_resources_in_try); + prepareElementsList(node.resources(), TokenNameSEMICOLON, TokenNameLPAREN); + handleWrap(this.options.alignment_for_resources_in_try); return true; } @Override public boolean visit(UnionType node) { List<Type> types = node.types(); - if (this.options.wrap_before_or_operator_multicatch && !types.isEmpty()) { + if (types.isEmpty()) + return true; + if (this.options.wrap_before_or_operator_multicatch) { for (Type type : types) { if (this.wrapIndexes.isEmpty()) { this.wrapIndexes.add(this.tm.firstIndexIn(type, -1)); } else { this.wrapIndexes.add(this.tm.firstIndexBefore(type, TokenNameOR)); + this.secondaryWrapIndexes.add(this.tm.firstIndexIn(type, -1)); } } - this.wrapParentIndex = this.tm.firstIndexBefore(node, TokenNameLPAREN); + this.wrapParentIndex = this.tm.firstIndexBefore(node, -1); + while (this.tm.get(this.wrapParentIndex).isComment()) + this.wrapParentIndex--; this.wrapGroupEnd = this.tm.lastIndexIn(types.get(types.size() - 1), -1); handleWrap(this.options.alignment_for_union_type_in_multicatch); } else { - handleArguments(types, this.options.alignment_for_union_type_in_multicatch); + prepareElementsList(types, TokenNameOR, TokenNameLPAREN); + handleWrap(this.options.alignment_for_union_type_in_multicatch); } return true; } @@ -528,16 +700,27 @@ * wrap executor will fix their indentation if necessary. */ private void forceContinuousWrapping(ASTNode node, int parentIndex) { + int parentIndent = this.tm.get(parentIndex).getIndent(); + int indentChange = -parentIndent; + int lineStart = this.tm.findFirstTokenInLine(parentIndex); + for (int i = parentIndex; i >= lineStart; i--) { + int align = this.tm.get(i).getAlign(); + if (align > 0) { + indentChange = -2 * parentIndent + align; + break; + } + } + + Token previous = null; int from = this.tm.firstIndexIn(node, -1); int to = this.tm.lastIndexIn(node, -1); - Token wrapParent = this.tm.get(parentIndex); - Token previous = null; for (int i = from; i <= to; i++) { Token token = this.tm.get(i); if ((token.getLineBreaksBefore() > 0 || (previous != null && previous.getLineBreaksAfter() > 0)) - && token.getWrapPolicy() == null) { - int indent = (token.getIndent() - wrapParent.getIndent()); - token.setWrapPolicy(new WrapPolicy(indent, parentIndex, true)); + && (token.getWrapPolicy() == null || token.getWrapPolicy().wrapMode == WrapMode.FORCED)) { + int extraIndent = token.getIndent() + indentChange; + token.setWrapPolicy(new WrapPolicy(WrapMode.FORCED, parentIndex, extraIndent)); + token.setIndent(parentIndent + extraIndent); } previous = token; } @@ -545,25 +728,33 @@ private void handleVariableDeclarations(List<VariableDeclarationFragment> fragments) { if (fragments.size() > 1) { - for (int i = 1; i < fragments.size(); i++) - this.wrapIndexes.add(this.tm.firstIndexIn(fragments.get(i), -1)); this.wrapParentIndex = this.tm.firstIndexIn(fragments.get(0), -1); - this.wrapGroupEnd = this.tm.lastIndexIn(fragments.get(fragments.size() - 1), -1); + prepareElementsList(fragments, TokenNameCOMMA, -1); + this.wrapIndexes.remove(0); handleWrap(this.options.alignment_for_multiple_fields); } } private void handleArguments(List<? extends ASTNode> arguments, int wrappingOption) { - for (ASTNode argument : arguments) - this.wrapIndexes.add(this.tm.firstIndexIn(argument, -1)); + this.wrapPenalties.add(1 / PREFERRED); + prepareElementsList(arguments, TokenNameCOMMA, TokenNameLPAREN); + handleWrap(wrappingOption); + } + + private void prepareElementsList(List<? extends ASTNode> elements, int separatorType, int wrapParentType) { + for (int i = 0; i < elements.size(); i++) { + ASTNode element = elements.get(i); + this.wrapIndexes.add(this.tm.firstIndexIn(element, -1)); + if (i > 0) + this.secondaryWrapIndexes.add(this.tm.firstIndexBefore(element, separatorType)); + } // wrapIndexes may have been filled with additional values even if arguments is empty if (!this.wrapIndexes.isEmpty()) { Token firstToken = this.tm.get(this.wrapIndexes.get(0)); - this.wrapParentIndex = this.tm.findIndex(firstToken.originalStart - 1, TokenNameLPAREN, false); - if (!arguments.isEmpty() && this.wrapGroupEnd < 0) - this.wrapGroupEnd = this.tm.lastIndexIn(arguments.get(arguments.size() - 1), -1); - assert this.wrapGroupEnd >= 0; - handleWrap(wrappingOption, 1 / PREFERRED); + if (this.wrapParentIndex < 0) + this.wrapParentIndex = this.tm.findIndex(firstToken.originalStart - 1, wrapParentType, false); + if (!elements.isEmpty() && this.wrapGroupEnd < 0) + this.wrapGroupEnd = this.tm.lastIndexIn(elements.get(elements.size() - 1), -1); } } @@ -577,17 +768,23 @@ } private void handleWrap(int wrappingOption, ASTNode parentNode) { + doHandleWrap(wrappingOption, parentNode); + this.wrapIndexes.clear(); + this.secondaryWrapIndexes.clear(); + this.wrapPenalties.clear(); + this.wrapParentIndex = this.wrapGroupEnd = -1; + } + + private void doHandleWrap(int wrappingOption, ASTNode parentNode) { if (this.wrapIndexes.isEmpty()) return; - assert this.wrapParentIndex >= 0; + assert this.wrapParentIndex >= 0 && this.wrapParentIndex < this.wrapIndexes.get(0); + assert this.wrapGroupEnd >= this.wrapIndexes.get(this.wrapIndexes.size() - 1); float penalty = this.wrapPenalties.isEmpty() ? 1 : this.wrapPenalties.get(0); WrapPolicy policy = getWrapPolicy(wrappingOption, penalty, true, parentNode); - if (policy == null) { - this.wrapIndexes.clear(); - this.wrapPenalties.clear(); - this.wrapParentIndex = this.wrapGroupEnd = -1; + if (policy == null) return; - } + setTokenWrapPolicy(this.wrapIndexes.get(0), policy, true); boolean wrapPreceedingComments = !(parentNode instanceof InfixExpression) @@ -604,20 +801,28 @@ boolean satisfied = false; for (int index : this.wrapIndexes) { Token token = this.tm.get(index); - if (token.getWrapPolicy().isTopPriority()) { + if (token.getWrapPolicy().wrapMode == WrapMode.TOP_PRIORITY) { token.breakBefore(); satisfied = true; } } if (!satisfied) { - boolean canWrapFirst = (wrappingOption & Alignment.M_NEXT_PER_LINE_SPLIT) != Alignment.M_NEXT_PER_LINE_SPLIT; + boolean canWrapFirst = (wrappingOption + & Alignment.M_NEXT_PER_LINE_SPLIT) != Alignment.M_NEXT_PER_LINE_SPLIT; if (canWrapFirst) this.tm.get(this.wrapIndexes.get(0)).breakBefore(); } } - this.wrapIndexes.clear(); - this.wrapPenalties.clear(); - this.wrapParentIndex = this.wrapGroupEnd = -1; + + if (!this.secondaryWrapIndexes.isEmpty()) { + int optionNoAlignment = (wrappingOption & ~Alignment.SPLIT_MASK) | Alignment.M_NO_ALIGNMENT; + policy = getWrapPolicy(optionNoAlignment, 1, false, parentNode); + for (int index : this.secondaryWrapIndexes) { + Token token = this.tm.get(index); + if (token.getWrapPolicy() == null) + token.setWrapPolicy(policy); + } + } } private void setTokenWrapPolicy(int index, WrapPolicy policy, boolean wrapPreceedingComments) { @@ -635,23 +840,12 @@ Token token = this.tm.get(index); token.setWrapPolicy(policy); - if (this.options.join_wrapped_lines - && (token.tokenType == TokenNameCOMMENT_BLOCK || token.tokenType == TokenNameCOMMENT_JAVADOC)) { + + if (this.options.join_wrapped_lines && token.tokenType == TokenNameCOMMENT_BLOCK) { // allow wrap preparator to decide if this comment should be wrapped token.clearLineBreaksBefore(); } - - // extend this policy to a token that is in the next line because of comments - for (int i = index + 1; i < this.tm.size(); i++) { - Token next = this.tm.get(i); - WrapPolicy policy2 = next.getWrapPolicy(); - if (policy2 != null && policy2.isForced && policy2.extraIndent == 0) { - next.setWrapPolicy(policy); - } else if (next.tokenType != TokenNameCOMMENT_LINE && next.tokenType != TokenNameCOMMENT_BLOCK) { - break; } - } - } private WrapPolicy getWrapPolicy(int wrappingOption, float penaltyMultiplier, boolean isFirst, ASTNode parentNode) { assert this.wrapParentIndex >= 0 && this.wrapGroupEnd >= 0; @@ -673,10 +867,12 @@ extraIndent = this.options.continuation_indentation_for_array_initializer; } + WrapMode wrapMode = WrapMode.WHERE_NECESSARY; boolean isTopPriority = false; switch (wrappingOption & Alignment.SPLIT_MASK) { case Alignment.M_NO_ALIGNMENT: - return null; + wrapMode = WrapMode.DISABLED; + break; case Alignment.M_COMPACT_FIRST_BREAK_SPLIT: isTopPriority = isFirst; break; @@ -695,16 +891,18 @@ if (isAlreadyWrapped) isTopPriority = false; // to avoid triggering top priority wrapping - int topPriorityGroupEnd = isTopPriority ? this.wrapGroupEnd : -1; + if (isTopPriority) + wrapMode = WrapMode.TOP_PRIORITY; extraIndent *= this.options.indentation_size; - return new WrapPolicy(extraIndent, this.wrapParentIndex, this.currentDepth, penaltyMultiplier, isFirst, - indentOnColumn, topPriorityGroupEnd, false); + return new WrapPolicy(wrapMode, this.wrapParentIndex, this.wrapGroupEnd, extraIndent, this.currentDepth, + penaltyMultiplier, isFirst, indentOnColumn); } - public void finishUp(ASTNode astRoot) { + public void finishUp(ASTNode astRoot, IRegion[] regions) { preserveExistingLineBreaks(); + if (regions != null) + applyBreaksOutsideRegions(regions); new WrapExecutor(this.tm, this.options).executeWraps(); - if (this.fieldAligner != null) this.fieldAligner.alignComments(); wrapComments(); fixEnumConstantIndents(astRoot); @@ -722,45 +920,26 @@ @Override protected boolean token(Token token, int index) { - int lineBreaks = getLineBreaksBetween(getPrevious(), token); - if (index > WrapPreparator.this.importsStart && index < WrapPreparator.this.importsEnd) { - lineBreaks = lineBreaks > 1 ? (this.options2.blank_lines_between_import_groups + 1) : 0; - } else { - lineBreaks = Math.min(lineBreaks, this.options2.number_of_empty_lines_to_preserve + 1); - } + boolean isBetweenImports = index > WrapPreparator.this.importsStart + && index < WrapPreparator.this.importsEnd; + int lineBreaks = getLineBreaksToPreserve(getPrevious(), token, isBetweenImports); if (lineBreaks <= getLineBreaksBefore()) return true; - if (!this.options2.join_wrapped_lines && token.isWrappable() && lineBreaks == 1) { + if (lineBreaks == 1) { + if ((!this.options2.join_wrapped_lines && token.isWrappable()) || index == 0) token.breakBefore(); } else if (lineBreaks > 1) { - if (index == 0) - lineBreaks--; token.putLineBreaksBefore(lineBreaks); } return true; } - private int getLineBreaksBetween(Token token1, Token token2) { - if (token1 != null) { - List<Token> structure1 = token1.getInternalStructure(); - if (structure1 != null && !structure1.isEmpty()) - token1 = structure1.get(structure1.size() - 1); - } - List<Token> structure2 = token2.getInternalStructure(); - if (structure2 != null && !structure2.isEmpty()) - token2 = structure2.get(0); - int lineBreaks = WrapPreparator.this.tm.countLineBreaksBetween(token1, token2); - if (token1 == null) - lineBreaks++; - return lineBreaks; - } }); Token last = this.tm.get(this.tm.size() - 1); last.clearLineBreaksAfter(); - int endingBreaks = this.tm.countLineBreaksBetween(last, null); - endingBreaks = Math.min(endingBreaks, this.options.number_of_empty_lines_to_preserve); + int endingBreaks = getLineBreaksToPreserve(last, null, false); if (endingBreaks > 0) { last.putLineBreaksAfter(endingBreaks); } else if ((this.kind & CodeFormatter.K_COMPILATION_UNIT) != 0 @@ -769,6 +948,53 @@ } } + int getLineBreaksToPreserve(Token token1, Token token2, boolean isBetweenImports) { + if (token1 != null) { + List<Token> structure = token1.getInternalStructure(); + if (structure != null && !structure.isEmpty()) + token1 = structure.get(structure.size() - 1); + } + if (token2 != null) { + List<Token> structure = token2.getInternalStructure(); + if (structure != null && !structure.isEmpty()) + token2 = structure.get(0); + } + int lineBreaks = WrapPreparator.this.tm.countLineBreaksBetween(token1, token2); + if (isBetweenImports) + return lineBreaks > 1 ? (this.options.blank_lines_between_import_groups + 1) : 0; + + int toPreserve = this.options.number_of_empty_lines_to_preserve; + if (token1 != null && token2 != null) + toPreserve++; // n empty lines = n+1 line breaks, except for file start and end + if (token1 != null && token1.tokenType == Token.TokenNameEMPTY_LINE) + toPreserve--; + return Math.min(lineBreaks, toPreserve); + } + + private void applyBreaksOutsideRegions(IRegion[] regions) { + String source = this.tm.getSource(); + int previousRegionEnd = 0; + for (IRegion region : regions) { + int index = this.tm.findIndex(previousRegionEnd, -1, true); + Token token = this.tm.get(index); + if (this.tm.countLineBreaksBetween(source, previousRegionEnd, + Math.min(token.originalStart, region.getOffset())) > 0) + token.breakBefore(); + for (index++; index < this.tm.size(); index++) { + Token next = this.tm.get(index); + if (next.originalStart > region.getOffset()) { + if (this.tm.countLineBreaksBetween(source, token.originalEnd, region.getOffset()) > 0) + next.breakBefore(); + break; + } + if (this.tm.countLineBreaksBetween(token, next) > 0) + next.breakBefore(); + token = next; + } + previousRegionEnd = region.getOffset() + region.getLength() - 1; + } + } + private void wrapComments() { CommentWrapExecutor commentWrapper = new CommentWrapExecutor(this.tm, this.options); boolean isNLSTagInLine = false;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java index fdc1e21..2ecd344 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
@@ -32,6 +32,7 @@ import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; @@ -368,19 +369,18 @@ signatureToReplace = originalSignature; break; case POSITION_RETURN_TYPE: - assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$ + assert originalSignature.charAt(0) == '(' || originalSignature.charAt(0) == '<': "signature must start with '(' or '<'"; //$NON-NLS-1$ int close = originalSignature.indexOf(')'); buf.append(originalSignature, 0, close+1); signatureToReplace = originalSignature.substring(close+1); break; default: // parameter SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations - wrapper.start = 1; + wrapper.start = CharOperation.indexOf('(', wrapper.signature) + 1; // possibly skipping type parameters for (int i = 0; i < updatePosition; i++) - wrapper.start = wrapper.computeEnd() + 1; + wrapper.start = wrapper.skipAngleContents(wrapper.computeEnd()) + 1; int start = wrapper.start; - int end = wrapper.computeEnd(); - end = wrapper.skipAngleContents(end); + int end = wrapper.skipAngleContents(wrapper.computeEnd()); buf.append(originalSignature, 0, start); signatureToReplace = originalSignature.substring(start, end+1); postfix = originalSignature.substring(end+1, originalSignature.length()); @@ -643,7 +643,7 @@ { String[] result = new String[4]; // prefix, orig, replacement, postfix StringBuffer buf; - assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$ + assert originalSignature.charAt(0) == '(' || originalSignature.charAt(0) == '<': "signature must start with '(' or '<'"; //$NON-NLS-1$ int close = originalSignature.indexOf(')'); result[0] = originalSignature.substring(0, close+1); buf = new StringBuffer(); @@ -675,12 +675,11 @@ String[] result = new String[4]; // prefix, orig, replacement, postfix StringBuffer buf; SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations - wrapper.start = 1; + wrapper.start = CharOperation.indexOf('(', wrapper.signature) + 1; // possibly skip type parameters for (int i = 0; i < paramIdx; i++) - wrapper.start = wrapper.computeEnd() + 1; + wrapper.start = wrapper.skipAngleContents(wrapper.computeEnd()) + 1; int start = wrapper.start; - int end = wrapper.computeEnd(); - end = wrapper.skipAngleContents(end); + int end = wrapper.skipAngleContents(wrapper.computeEnd()); result[0] = originalSignature.substring(0, start); buf = new StringBuffer(); result[1] = originalSignature.substring(start, end+1);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java index 9a6cee6..61d4a55 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/parser/TypeConverter.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2013 IBM Corporation and others. + * Copyright (c) 2008, 2015 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 @@ -113,7 +113,7 @@ int length = typeName.length; this.namePos = 0; - return decodeType(typeName, length, start, end, true); + return decodeType2(typeName, length, start, end, true); } /* @@ -126,7 +126,7 @@ int length = typeName.length; this.namePos = 0; - return decodeType(typeName, length, start, end, false); + return decodeType2(typeName, length, start, end, false); } /* @@ -366,7 +366,7 @@ } } - private TypeReference decodeType(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) { + private TypeReference decodeType2(char[] typeName, int length, int start, int end, boolean includeGenericsAnyway) { int identCount = 1; int dim = 0; int nameFragmentStart = this.namePos, nameFragmentEnd = -1; @@ -388,7 +388,7 @@ } this.namePos += max; Wildcard result = new Wildcard(Wildcard.SUPER); - result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway); + result.bound = decodeType2(typeName, length, start, end, includeGenericsAnyway); result.sourceStart = start; result.sourceEnd = end; return result; @@ -404,7 +404,7 @@ } this.namePos += max; Wildcard result = new Wildcard(Wildcard.EXTENDS); - result.bound = decodeType(typeName, length, start, end, includeGenericsAnyway); + result.bound = decodeType2(typeName, length, start, end, includeGenericsAnyway); result.sourceStart = start; result.sourceEnd = end; return result; @@ -460,6 +460,18 @@ } this.namePos++; } + return decodeType3(typeName, length, start, end, identCount, dim, nameFragmentStart, nameFragmentEnd, + fragments); + } + + /* + * Method should be inlined. + * + * Only extracted to work around https://bugs.eclipse.org/471835 : + * Random crashes in PhaseIdealLoop::build_loop_late_post when C2 JIT tries to compile TypeConverter::decodeType + */ + private TypeReference decodeType3(char[] typeName, int length, int start, int end, int identCount, int dim, + int nameFragmentStart, int nameFragmentEnd, ArrayList fragments) { if (nameFragmentEnd < 0) nameFragmentEnd = this.namePos-1; if (fragments == null) { // non parameterized /* rebuild identifiers and dimensions */ @@ -542,7 +554,7 @@ ArrayList argumentList = new ArrayList(1); int count = 0; argumentsLoop: while (this.namePos < length) { - TypeReference argument = decodeType(typeName, length, start, end, includeGenericsAnyway); + TypeReference argument = decodeType2(typeName, length, start, end, includeGenericsAnyway); count++; argumentList.add(argument); if (this.namePos >= length) break argumentsLoop;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java index 6941961..d085650 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CommentRecorderParser.java
@@ -88,6 +88,13 @@ } /* (non-Javadoc) + * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeAnnotationTypeDeclarationHeader() + */ + protected void consumeAnnotationTypeDeclarationHeader() { + pushOnCommentsStack(0, this.scanner.commentPtr); + super.consumeAnnotationTypeDeclarationHeader(); + } + /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeClassHeader() */ protected void consumeClassHeader() { @@ -102,6 +109,22 @@ super.consumeEmptyTypeDeclaration(); } /* (non-Javadoc) + * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeEnterAnonymousClassBody(boolean) + */ + @Override + protected void consumeEnterAnonymousClassBody(boolean qualified) { + pushOnCommentsStack(0, this.scanner.commentPtr); + super.consumeEnterAnonymousClassBody(qualified); + } + /* (non-Javadoc) + * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeEnumHeader() + */ + protected void consumeEnumHeader() { + pushOnCommentsStack(0, this.scanner.commentPtr); + super.consumeEnumHeader(); + } + + /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeInterfaceHeader() */ protected void consumeInterfaceHeader() {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java index a5647e0..f24bba1 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2015 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 @@ -1165,6 +1165,7 @@ this.lookupEnvironment = new LookupEnvironment(this, this.options, problemReporter, this.nameEnvironment); this.lookupEnvironment.mayTolerateMissingType = true; this.parser = MatchLocatorParser.createParser(problemReporter, this); + this.bindings = new SimpleLookupTable(); // For every LE // basic parser needs also to be reset as project options may have changed // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=163072