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