Skip to content

Commit dabc69f

Browse files
authored
Merge branch 'eclipse-jdt:master' into master
2 parents 2f6b62e + 504752c commit dabc69f

4 files changed

Lines changed: 228 additions & 3 deletions

File tree

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ protected boolean commentParse() {
375375
pushText(this.textStart, textEndPosition);
376376
}
377377
refreshInlineTagPosition(previousPosition);
378+
} else if ((this.source[this.index] == '\n' || this.source[this.index] == '\r') && !shouldAbortDueToJavadocTag(previousPosition) ) {
379+
pushText(previousPosition, this.index); // Enables adding closing curly brackets to node elements in Javadoc when the TagElement spans multiple lines
378380
}
379381
if (!isFormatterParser && !treatAsText && (!this.inlineReturn || this.inlineReturnOpenBraces <= 0))
380382
this.textStart = this.index;
@@ -409,6 +411,9 @@ protected boolean commentParse() {
409411
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: count opening braces when ignoring tags
410412
if (considerTagAsPlainText) {
411413
openingBraces++;
414+
if (this.source[this.index] == '\n' || this.source[this.index] == '\r') {
415+
pushText(this.textStart, this.index); // Enables adding opening curly brackets to node elements in Javadoc when the TagElement spans multiple lines
416+
}
412417
} else if (this.inlineTagStarted) {
413418
if (this.tagValue == TAG_RETURN_VALUE) {
414419
this.inlineReturn= true;
@@ -555,6 +560,35 @@ protected boolean commentParse() {
555560
return validComment;
556561
}
557562

563+
/**
564+
* Scans backwards from current position to find if `{ @` pattern exists
565+
* before a newline. Returns true immediately when pattern is found.
566+
*/
567+
protected boolean shouldAbortDueToJavadocTag(int currPos) {
568+
int pos = currPos - 1;
569+
if (this.source == null || pos < 0 || pos >= this.source.length) {
570+
return false;
571+
}
572+
573+
while (pos >= 0) {
574+
char currentChar = this.source[pos];
575+
576+
// If encounter a newline, stop scanning
577+
if (currentChar == '\n' || currentChar == '\r') {
578+
pos--;
579+
break;
580+
}
581+
582+
// Check for pattern
583+
if (currentChar == '@' && pos > 0 && this.source[pos - 1] == '{') {
584+
return true;
585+
}
586+
587+
pos--;
588+
}
589+
return false;
590+
}
591+
558592
protected void addFragmentToInlineReturn() {
559593
// do nothing
560594
}

org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuildpathTests.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,24 +1018,52 @@ public void test0100() throws JavaModelException {
10181018
}
10191019

10201020
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=143025
1021+
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4745
10211022
public void testMissingOutputFolder() throws JavaModelException {
10221023
IPath projectPath = env.addProject("P"); //$NON-NLS-1$
1024+
IJavaProject p = env.getJavaProject(projectPath);
10231025
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$
1024-
env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$
1026+
IPath root = env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$
10251027
IPath bin = env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$
1026-
1028+
env.addExternalJars(projectPath, Util.getJavaClassLibs());
1029+
env.addClass(root, "q", "Y", """
1030+
package q;
1031+
public class Y {}
1032+
""");
10271033
fullBuild();
10281034
expectingNoProblems();
1035+
IPath expectedClassFilePath = bin.append("q").append("Y.class");
1036+
expectingPresenceOf(new IPath[]{ expectedClassFilePath });
1037+
1038+
env.removeFolder(bin);
1039+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
1040+
1041+
incrementalBuild();
1042+
expectingNoProblems();
1043+
expectingPresenceOf(bin); // check that bin folder was recreated and is marked as derived
1044+
if (!env.getProject(projectPath).getFolder("bin").isDerived()) {
1045+
fail("output folder is not derived");
1046+
}
1047+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
1048+
1049+
// now enable the (disabled by default) preference and try again
1050+
p.setOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, JavaCore.ENABLED);
10291051

10301052
env.removeFolder(bin);
1053+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
10311054

10321055
incrementalBuild();
10331056
expectingNoProblems();
10341057
expectingPresenceOf(bin); // check that bin folder was recreated and is marked as derived
1035-
if (!env.getProject(projectPath).getFolder("bin").isDerived())
1058+
if (!env.getProject(projectPath).getFolder("bin").isDerived()) {
10361059
fail("output folder is not derived");
1060+
}
1061+
1062+
expectingPresenceOf(new IPath[]{ expectedClassFilePath });
10371063
env.removeProject(projectPath);
10381064
}
1065+
1066+
10391067
@Override
10401068
protected void tearDown() throws Exception {
10411069
super.tearDown();

org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterJavadocTest.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3482,6 +3482,10 @@ public class X {}
34823482
TagElement tagElement = (TagElement) docComment.tags().get(0);
34833483
List<TagElement> listFrag = tagElement.fragments();
34843484
assumeEquals("wrong number of tags", 1, listFrag.size());
3485+
assumeEquals("Invalid content", "{@code public class Example { final int a = 1; } }", listFrag.get(0).toString());
3486+
List<TextElement> textElementFrag = listFrag.get(0).fragments();
3487+
assumeEquals("wrong number of frags", 1, listFrag.size());
3488+
assumeEquals("Invalid content", " public class Example { final int a = 1; } ", textElementFrag.get(0).getText());
34853489
}
34863490

34873491
public void testContentOfCodeParsedOutside4615_02() throws JavaModelException {
@@ -3507,6 +3511,9 @@ public class X {}
35073511
assumeEquals("wrong number of frags", 1, frags.size());
35083512
TagElement problematicTag = (TagElement) frags.get(0);
35093513
assumeEquals("invalid content", "{@code com/{filename:\\w+}.jsp}", problematicTag.toString());
3514+
List<TextElement> textElementFrag = problematicTag.fragments();
3515+
assumeEquals("wrong number of frags", 1, textElementFrag.size());
3516+
assumeEquals("Invalid content", " com/{filename:\\w+}.jsp", textElementFrag.get(0).getText());
35103517
}
35113518

35123519
public void testContentOfCodeParsedOutside4615_03() throws JavaModelException {
@@ -3531,6 +3538,9 @@ public class X {}
35313538
List<TagElement> listFrag = tagElement.fragments();
35323539
assumeEquals("wrong number of tags", 1, listFrag.size());
35333540
assumeEquals("Invalid content", "{@code public class X { void foo() { int x; } } }", listFrag.get(0).toString());
3541+
List<TextElement> textElementFrag = listFrag.get(0).fragments();
3542+
assumeEquals("wrong number of frags", 1, textElementFrag.size());
3543+
assumeEquals("Invalid content", " public class X { void foo() { int x; } } ", textElementFrag.get(0).getText());
35343544
}
35353545

35363546
public void testContentOfCodeParsedOutside4615_04() throws JavaModelException {
@@ -3555,6 +3565,9 @@ public class X {}
35553565
List<TagElement> listFrag = tagElement.fragments();
35563566
assumeEquals("wrong number of tags", 1, listFrag.size());
35573567
assumeEquals("Invalid content", "{@code public class Example { final int sasi; } class B{}}", listFrag.get(0).toString());
3568+
List<TextElement> textElementFrag = listFrag.get(0).fragments();
3569+
assumeEquals("wrong number of frags", 1, textElementFrag.size());
3570+
assumeEquals("Invalid content", " public class Example { final int sasi; } class B{}", textElementFrag.get(0).getText());
35583571
}
35593572

35603573
//code tag in multiple lines
@@ -3582,5 +3595,99 @@ public class X {}
35823595
assumeEquals("wrong number of frags", 1, frags.size());
35833596
TagElement problematicTag = (TagElement) frags.get(0);
35843597
assumeEquals("invalid content", "{@code com/{filename:\\w+}.jsp}", problematicTag.toString());
3598+
List<TextElement> textElementFrag = problematicTag.fragments();
3599+
assumeEquals("wrong number of frags", 2, textElementFrag.size());
3600+
assumeEquals("Invalid first textElement content", " com/{filename:\\w+}", textElementFrag.get(0).getText());
3601+
assumeEquals("Invalid second textElement content", ".jsp", textElementFrag.get(1).getText());
3602+
}
3603+
3604+
public void testMultulineCodeDropCurlyBrackets4683_01() throws JavaModelException {
3605+
this.workingCopies = new ICompilationUnit[1];
3606+
this.astLevel = AST.JLS25;
3607+
this.workingCopies[0] = getWorkingCopy("/Converter25/src/javadoc/Markdown.java",
3608+
"""
3609+
/**
3610+
* Performs:
3611+
* <pre>{@code
3612+
* for (String s : strings) {
3613+
* if (s.equals(value)) {
3614+
* return 0;
3615+
* }
3616+
* if (s.startsWith(value)) {
3617+
* return 1;
3618+
* }
3619+
* }
3620+
* return -1;
3621+
* }</pre>
3622+
*/
3623+
public class Markdown {}
3624+
"""
3625+
);
3626+
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
3627+
List unitComments = compilUnit.getCommentList();
3628+
assertEquals("Wrong number of comments", 1, unitComments.size());
3629+
Comment comment = (Comment) unitComments.get(0);
3630+
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
3631+
Javadoc docComment = (Javadoc) compilUnit.getCommentList().get(0);
3632+
assumeEquals("wrong number of tags", 1, docComment.tags().size());
3633+
TagElement parentTag = (TagElement) docComment.tags().get(0);
3634+
List<?> frags = parentTag.fragments();
3635+
TagElement thirdTag = (TagElement) frags.get(2);
3636+
List<TextElement> textFrags = thirdTag.fragments();
3637+
assumeEquals("wrong number of TextElements", 9, textFrags.size());
3638+
assumeEquals("Invalid first TextElement content","for (String s : strings) {" , textFrags.get(0).getText());
3639+
assumeEquals("Invalid second TextElement content","if (s.equals(value)) {" , textFrags.get(1).getText());
3640+
assumeEquals("Invalid third TextElement content","return 0;" , textFrags.get(2).getText());
3641+
assumeEquals("Invalid fourth TextElement content","}" , textFrags.get(3).getText());
3642+
assumeEquals("Invalid fifth TextElement content","if (s.startsWith(value)) {" , textFrags.get(4).getText());
3643+
assumeEquals("Invalid sixth TextElement content","return 1;" , textFrags.get(5).getText());
3644+
assumeEquals("Invalid seventh TextElement content","}" , textFrags.get(6).getText());
3645+
assumeEquals("Invalid eighth TextElement content","}" , textFrags.get(7).getText());
3646+
assumeEquals("Invalid nineth TextElement content","return -1;" , textFrags.get(8).getText());
3647+
}
3648+
3649+
public void testMultulineCodeDropCurlyBrackets4683_02() throws JavaModelException {
3650+
this.workingCopies = new ICompilationUnit[1];
3651+
this.astLevel = AST.JLS25;
3652+
this.workingCopies[0] = getWorkingCopy("/Converter25/src/javadoc/Markdown.java",
3653+
"""
3654+
/**
3655+
* Performs:
3656+
* <pre>{@literal
3657+
* for (String s : strings) {
3658+
* if (s.equals(value)) {
3659+
* return 0;
3660+
* }
3661+
* if (s.startsWith(value)) {
3662+
* return 1;
3663+
* }
3664+
* }
3665+
* return -1;
3666+
* }</pre>
3667+
*/
3668+
public class Markdown {}
3669+
"""
3670+
);
3671+
CompilationUnit compilUnit = (CompilationUnit) runConversion(this.workingCopies[0], true);
3672+
List unitComments = compilUnit.getCommentList();
3673+
assertEquals("Wrong number of comments", 1, unitComments.size());
3674+
Comment comment = (Comment) unitComments.get(0);
3675+
assertEquals("Comment should be javadoc", comment.getNodeType(), ASTNode.JAVADOC);
3676+
Javadoc docComment = (Javadoc) compilUnit.getCommentList().get(0);
3677+
assumeEquals("wrong number of tags", 1, docComment.tags().size());
3678+
TagElement parentTag = (TagElement) docComment.tags().get(0);
3679+
List<?> frags = parentTag.fragments();
3680+
TagElement thirdTag = (TagElement) frags.get(2);
3681+
List<TextElement> textFrags = thirdTag.fragments();
3682+
assumeEquals("wrong number of TextElements", 9, textFrags.size());
3683+
assumeEquals("Invalid first TextElement content","for (String s : strings) {" , textFrags.get(0).getText());
3684+
assumeEquals("Invalid second TextElement content","if (s.equals(value)) {" , textFrags.get(1).getText());
3685+
assumeEquals("Invalid third TextElement content","return 0;" , textFrags.get(2).getText());
3686+
assumeEquals("Invalid fourth TextElement content","}" , textFrags.get(3).getText());
3687+
assumeEquals("Invalid fifth TextElement content","if (s.startsWith(value)) {" , textFrags.get(4).getText());
3688+
assumeEquals("Invalid sixth TextElement content","return 1;" , textFrags.get(5).getText());
3689+
assumeEquals("Invalid seventh TextElement content","}" , textFrags.get(6).getText());
3690+
assumeEquals("Invalid eighth TextElement content","}" , textFrags.get(7).getText());
3691+
assumeEquals("Invalid nineth TextElement content","return -1;" , textFrags.get(8).getText());
35853692
}
35863693
}

org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map.Entry;
2929
import java.util.Set;
3030
import org.eclipse.core.resources.ICommand;
31+
import org.eclipse.core.resources.IContainer;
3132
import org.eclipse.core.resources.IMarker;
3233
import org.eclipse.core.resources.IProject;
3334
import org.eclipse.core.resources.IResource;
@@ -42,6 +43,7 @@
4243
import org.eclipse.jdt.core.IClasspathEntry;
4344
import org.eclipse.jdt.core.IJavaModelMarker;
4445
import org.eclipse.jdt.core.IJavaModelStatusConstants;
46+
import org.eclipse.jdt.core.IJavaProject;
4547
import org.eclipse.jdt.core.JavaCore;
4648
import org.eclipse.jdt.core.JavaModelException;
4749
import org.eclipse.jdt.core.compiler.CategorizedProblem;
@@ -686,6 +688,11 @@ private int initializeBuilder(int kind, boolean forBuild) throws CoreException {
686688
builtProjects = new LinkedHashSet<>();
687689
}
688690
builtProjects.add(projectName);
691+
692+
if (kind != CLEAN_BUILD && kind != FULL_BUILD) {
693+
// check if we need to switch to full build due to missing output folder(s)
694+
kind = checkOutputFolders(this.javaProject, kind);
695+
}
689696
}
690697

691698
this.binaryLocationsPerProject = new HashMap<>(3);
@@ -878,6 +885,55 @@ private void recordNewState(State state) {
878885
JavaModelManager.getJavaModelManager().setLastBuiltState(this.currentProject, state);
879886
}
880887

888+
/**
889+
* Checks whether all output folders for the given project exist on disk.
890+
*
891+
* @param project
892+
* The Java project to check
893+
* @return If any output folder is missing, {@link IncrementalProjectBuilder#FULL_BUILD} is returned and original
894+
* <code>buildKind</code> argument otherwise.
895+
*/
896+
protected int checkOutputFolders(IJavaProject project, int buildKind) {
897+
if (!JavaCore.ENABLED
898+
.equals(project.getOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, true))) {
899+
return buildKind;
900+
}
901+
try {
902+
IWorkspaceRoot root = project.getProject().getWorkspace().getRoot();
903+
for (IClasspathEntry entry : project.getRawClasspath()) {
904+
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
905+
continue;
906+
}
907+
IPath outputLocation = entry.getOutputLocation();
908+
if (outputLocation != null) {
909+
IContainer outputContainer;
910+
if(outputLocation.segmentCount() == 1) {
911+
outputContainer = project.getProject();
912+
} else {
913+
outputContainer = root.getFolder(outputLocation);
914+
}
915+
if (!existsOnDisk(outputContainer)) {
916+
return FULL_BUILD;
917+
}
918+
}
919+
}
920+
} catch (JavaModelException e) {
921+
// If we can't read the classpath, just return the original build kind
922+
}
923+
return buildKind;
924+
}
925+
926+
private static boolean existsOnDisk(IContainer defaultOutputLocation) {
927+
boolean exists = defaultOutputLocation.exists();
928+
// Resource is not present in workspace model
929+
if (!exists) {
930+
return false;
931+
}
932+
// check that the folder *really* exists on disk and is a folder
933+
IPath location = defaultOutputLocation.getLocation();
934+
return location != null && location.toFile().isDirectory();
935+
}
936+
881937
/**
882938
* String representation for debugging purposes
883939
*/

0 commit comments

Comments
 (0)