diff --git a/javacc-wrapper.xml b/javacc-wrapper.xml index c7f1d7eead..c92827a482 100644 --- a/javacc-wrapper.xml +++ b/javacc-wrapper.xml @@ -246,6 +246,11 @@ + + + + + diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java index 6605aef54c..180b04e6f5 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java @@ -9,6 +9,7 @@ import net.sourceforge.pmd.lang.apex.ApexJorjeLogging; import net.sourceforge.pmd.lang.apex.ApexLanguageProcessor; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.document.FileLocation; import apex.jorje.data.Locations; import apex.jorje.semantic.ast.compilation.Compilation; @@ -32,7 +33,8 @@ public final class ApexParser implements Parser { final ApexTreeBuilder treeBuilder = new ApexTreeBuilder(task, (ApexLanguageProcessor) task.getLanguageProcessor()); return treeBuilder.buildTree(astRoot); } catch (apex.jorje.services.exception.ParseException e) { - throw new ParseException(e).setFileName(task.getTextDocument().getPathId()); + FileLocation loc = FileLocation.caret(task.getTextDocument().getPathId(), e.getLoc().getLine(), e.getLoc().getColumn()); + throw new ParseException(e).withLocation(loc); } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java index b702fde142..f85f739734 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java @@ -58,7 +58,7 @@ public abstract class JavaCCTokenizer implements Tokenizer { currentToken = tokenFilter.getNextToken(); } } catch (FileAnalysisException e) { - throw e.setFileName(textFile.getPathId()); + throw e.setFileId(textFile.getPathId()); } finally { tokenEntries.add(TokenEntry.getEOF()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java index a0d869b740..6263d3a945 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/FileAnalysisException.java @@ -8,7 +8,9 @@ import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.PathId; /** @@ -22,7 +24,7 @@ import net.sourceforge.pmd.lang.document.PathId; */ public class FileAnalysisException extends RuntimeException { - private PathId filename = PathId.UNKNOWN; + private PathId fileId = PathId.UNKNOWN; public FileAnalysisException() { super(); @@ -40,24 +42,24 @@ public class FileAnalysisException extends RuntimeException { super(message, cause); } - public FileAnalysisException setFileName(PathId filename) { - this.filename = Objects.requireNonNull(filename); + public FileAnalysisException setFileId(PathId fileId) { + this.fileId = Objects.requireNonNull(fileId); return this; } protected boolean hasFileName() { - return !PathId.UNKNOWN.equals(filename); + return !PathId.UNKNOWN.equals(fileId); } /** * The name of the file in which the error occurred. */ - public @NonNull PathId getFileName() { - return filename; + public @NonNull PathId getFileId() { + return fileId; } @Override - public String getMessage() { + public final String getMessage() { return errorKind() + StringUtils.uncapitalize(positionToString()) + ": " + super.getMessage(); } @@ -65,11 +67,20 @@ public class FileAnalysisException extends RuntimeException { return "Error"; } - protected String positionToString() { + protected @Nullable FileLocation location() { + return null; + } + + private String positionToString() { + String result = ""; if (hasFileName()) { - return " in file '" + getFileName() + "'"; + result += " in file '" + getFileId().getOriginalPath() + "'"; } - return ""; + FileLocation loc = location(); + if (loc != null) { + result += loc.startPosToString(); + } + return result; } @@ -85,11 +96,11 @@ public class FileAnalysisException extends RuntimeException { */ public static FileAnalysisException wrap(@NonNull PathId filename, @NonNull String message, @NonNull Throwable cause) { if (cause instanceof FileAnalysisException) { - return ((FileAnalysisException) cause).setFileName(filename); + return ((FileAnalysisException) cause).setFileId(filename); } String fullMessage = "In file '" + filename + "': " + message; - return new FileAnalysisException(fullMessage, cause).setFileName(filename); + return new FileAnalysisException(fullMessage, cause).setFileId(filename); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java index 8b27689406..1cbf565158 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/ParseException.java @@ -14,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.reporting.Reportable; import net.sourceforge.pmd.util.StringUtil; public class ParseException extends FileAnalysisException { @@ -23,26 +24,16 @@ public class ParseException extends FileAnalysisException { * this object has been created due to a parse error, the token * followng this token will (therefore) be the first error token. */ - public final @Nullable GenericToken currentToken; - - public ParseException() { - super(); - this.currentToken = null; - } + private @Nullable FileLocation location; public ParseException(String message) { super(message); - this.currentToken = null; + this.location = null; } public ParseException(Throwable cause) { super(cause); - this.currentToken = null; - } - - public ParseException(String message, JavaccToken token) { - super(message); - this.currentToken = token; + this.location = null; } /** @@ -51,14 +42,30 @@ public class ParseException extends FileAnalysisException { public ParseException(@NonNull JavaccToken currentTokenVal, int[][] expectedTokenSequencesVal) { super(makeMessage(currentTokenVal, expectedTokenSequencesVal)); - currentToken = currentTokenVal; + location = currentTokenVal.getNext().getReportLocation(); } + public ParseException withLocation(FileLocation loc) { + location = loc; + super.setFileId(loc.getFileId()); + return this; + } + + public ParseException withLocation(Reportable reportable) { + return withLocation(reportable.getReportLocation()); + } + + @Override protected String errorKind() { return "Parse exception"; } + @Override + protected @Nullable FileLocation location() { + return location; + } + /** * It uses "currentToken" and "expectedTokenSequences" to generate a parse * error message and returns it. If this object has been created @@ -123,8 +130,6 @@ public class ParseException extends FileAnalysisException { if (maxSize > 1) { retval.append(']'); } - FileLocation loc = currentToken.next.getReportLocation(); - retval.append(" at ").append(loc.getStartPos().toDisplayStringInEnglish()); retval.append('.').append(eol); if (expectedTokenSequences.length == 1) { retval.append("Was expecting:").append(eol).append(" "); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java index 61202727ef..1b24be4fed 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/TokenMgrError.java @@ -4,9 +4,11 @@ package net.sourceforge.pmd.lang.ast; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.PathId; import net.sourceforge.pmd.util.StringUtil; @@ -32,7 +34,7 @@ public final class TokenMgrError extends FileAnalysisException { this.line = line; this.column = column; if (filename != null) { - super.setFileName(filename); + super.setFileId(filename); } } @@ -55,8 +57,8 @@ public final class TokenMgrError extends FileAnalysisException { } @Override - protected String positionToString() { - return super.positionToString() + " at line " + line + ", column " + column; + protected @NonNull FileLocation location() { + return FileLocation.caret(getFileId(), line, column); } @Override @@ -67,13 +69,13 @@ public final class TokenMgrError extends FileAnalysisException { /** * Replace the file name of this error. * - * @param filename New filename + * @param fileId New filename * * @return A new exception */ @Override - public TokenMgrError setFileName(PathId filename) { - super.setFileName(filename); + public TokenMgrError setFileId(PathId fileId) { + super.setFileId(fileId); return this; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java index 9720acde9b..85dfe0b770 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JjtreeParserAdapter.java @@ -35,7 +35,7 @@ public abstract class JjtreeParserAdapter implements Parser // Finally, do the parsing return parseImpl(charStream, task); } catch (FileAnalysisException tme) { - throw tme.setFileName(task.getTextDocument().getPathId()); + throw tme.setFileId(task.getTextDocument().getPathId()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java index 664198d9dd..05927d6d90 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/MalformedSourceException.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.ast.impl.javacc; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.document.FileLocation; @@ -20,12 +22,12 @@ public class MalformedSourceException extends FileAnalysisException { public MalformedSourceException(String message, Throwable cause, FileLocation fileLocation) { super(message, cause); this.location = Objects.requireNonNull(fileLocation); - setFileName(fileLocation.getFileId()); + setFileId(fileLocation.getFileId()); } @Override - protected String positionToString() { - return super.positionToString() + " at " + location.startPosToString(); + protected @NonNull FileLocation location() { + return location; } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java index 6328a65c06..60732a4e2d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java @@ -33,7 +33,7 @@ public final class CpdCompat { public static TextFile cpdCompat(SourceCode sourceCode) { return TextFile.forCharSeq( sourceCode.getCodeBuffer(), - PathId.fromPathLikeString("fname1.dummy"), + PathId.fromPathLikeString(sourceCode.getFileName()), dummyVersion() ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index e2060e3a8f..ef3ac7f0de 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -28,7 +28,6 @@ import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.internal.util.IOUtil; -import net.sourceforge.pmd.lang.document.PathId; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.util.StringUtil; @@ -188,11 +187,6 @@ public class XMLRenderer extends AbstractIncrementingRenderer { } } - @Override - protected String determineFileName(PathId fileId) { - return super.determineFileName(fileId); - } - @Override public void end() throws IOException { try { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java index 6e6ae326cd..24d162c0ff 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/GlobalListenerTest.java @@ -112,7 +112,7 @@ class GlobalListenerTest { runPmd(config, listener, rule); }); - assertEquals("fname1.dummy", exception.getFileName().getOriginalPath()); + assertEquals("fname1.dummy", exception.getFileId().getOriginalPath()); // cache methods are called regardless verify(mockCache).checkValidity(any(), any(), any()); diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index dbd990f157..8461a07438 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -311,13 +311,7 @@ class JavaParserImpl { } private void throwParseException(String message) { - int line = -1; - int col = -1; - if (jj_lastpos != null) { - line = jj_lastpos.beginLine; - col = jj_lastpos.beginColumn; - } - throw new ParseException("Line " + line + ", Column " + col + ": " + message); + throw new ParseException(message).withLocation(token); } @@ -1105,30 +1099,18 @@ void ImplementsList(): } void PermittedSubclasses() #PermitsList: +{} { - Token t; -} -{ - t = { - if (!"permits".equals(t.image)) { - throw new ParseException("ERROR: expecting permits"); - } - } + softKeyword("permits") ClassOrInterfaceType() ( "," ClassOrInterfaceType() )* } void EnumDeclaration(): +{} { - JavaccToken t; -} -{ - t = { - if (!"enum".equals(t.image)) { - throw new ParseException("ERROR: expecting enum"); - } - } - t= {jjtThis.setImage(t.image);} + softKeyword("enum") + {setLastTokenImage(jjtThis);} [ ImplementsList() ] EnumBody() } @@ -1150,16 +1132,10 @@ void EnumConstant(): } void RecordDeclaration(): +{} { - JavaccToken t; -} -{ - t = { - if (!"record".equals(t.image)) { - throw new ParseException("ERROR: expecting record"); - } - } - t= {jjtThis.setImage(t.image);} + softKeyword("record") + {setLastTokenImage(jjtThis);} [ TypeParameters() ] RecordHeader() [ ImplementsList() ] @@ -2493,16 +2469,9 @@ void CaseLabelElement(ASTSwitchLabel label) #void: } void Guard() #SwitchGuard: +{} { - Token t; -} -{ - t = { - if (!"when".equals(t.image)) { - throw new ParseException("ERROR: expected 'when'"); - } - } - + softKeyword("when") Expression() } @@ -2890,3 +2859,10 @@ void VariableAccess(): {} { } void TypeExpression(): {} { } void PatternExpression(): {} { } void LocalClassStatement(): {} { TypeDeclaration() } + +void softKeyword(String name) #void: {} { + { + if (!getToken(0).getImageCs().contentEquals(name)) + throwParseException("Expecting keyword '" + name + "'"); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/ReportingStrategy.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/ReportingStrategy.java index 2fc8e0a895..4ee3a41fcf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/ReportingStrategy.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/ReportingStrategy.java @@ -52,7 +52,7 @@ public interface ReportingStrategy { @Override public void report(Node node, String message, Void acc) { - throw new ParseException(message).setFileName(node.getTextDocument().getPathId()); + throw new ParseException(message).withLocation(node); } }; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java index 0335406aef..7bf3afb56b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java @@ -4,10 +4,11 @@ package net.sourceforge.pmd.lang.java.ast; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -309,7 +310,7 @@ class JDKVersionTest extends BaseJavaTreeDumpTest { @Test void jdk7PrivateMethodInnerClassInterface2() { ParseException thrown = assertThrows(ParseException.class, () -> java7.parseResource("private_method_in_inner_class_interface2.java")); - assertTrue(thrown.getMessage().contains("line 19")); + assertThat(thrown.getMessage(), containsString("line 19")); } @Override diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index 41fbefea0c..057bdb85c5 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -719,9 +719,8 @@ ASTMethodDeclarator MethodDeclarator() : { throw new ParseException("FUNCTION must RETURN a value or must be WRAPPED : found \"" + nextToken.getImage() - + "\" at line "+nextToken.getBeginLine() - + ", column "+nextToken.getBeginColumn() - ); + + "\"" + ).withLocation(nextToken); } } // There is no RETURN for a WRAPPED object @@ -5428,9 +5427,8 @@ void KEYWORD(String id) #void: { if (!token.getImage().equalsIgnoreCase(id)) { String eol = System.getProperty("line.separator", "\n"); - throw new ParseException("Encountered \"" + token.getImage() + "\" at line " - + token.getBeginLine() + ", column " + token.getBeginColumn() + "." + eol - + "Was expecting: " + id); + throw new ParseException("Encountered \"" + token.getImage() + "\" " + + "Was expecting: " + id).withLocation(token); } } } diff --git a/pmd-vm/etc/grammar/Vm.jjt b/pmd-vm/etc/grammar/Vm.jjt index 678ed8de31..5492f2b98e 100644 --- a/pmd-vm/etc/grammar/Vm.jjt +++ b/pmd-vm/etc/grammar/Vm.jjt @@ -66,6 +66,9 @@ import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; */ public class VmParserImpl { + private void throwParseException(String message) { + throw new ParseException(message).withLocation(token); + } /** * Check whether there is a left parenthesis with leading optional * whitespaces. This method is used in the semantic look ahead of @@ -1174,14 +1177,14 @@ VmNode Directive() : else if (!jjtThis.isDirective()) { // not a real directive, but maybe a Velocimacro - throw new ParseException("Invalid arg #" - + argPos + " in VM " + t.getImage(), t); + throwParseException("Invalid arg #" + + argPos + " in VM " + t.getImage()); } /* if #foreach and it's the 2nd arg, ok */ else if (jjtThis.isDirective() && (!"foreach".equals(jjtThis.getDirectiveName()) || argPos != 1)) { - throw new ParseException("Invalid arg #" - + argPos + " in directive " + t.getImage(), t); + throwParseException("Invalid arg #" + + argPos + " in directive " + t.getImage()); } else { @@ -1195,9 +1198,9 @@ VmNode Directive() : { /* if a VM and it's the 0th arg, not ok */ - throw new ParseException("Invalid first arg" + throwParseException("Invalid first arg" + " in #macro() directive - must be a" - + " word token (no ' or double quote surrounding)", t); + + " word token (no ' or double quote surrounding)"); } } @@ -1218,7 +1221,7 @@ VmNode Directive() : { // VELOCITY-667 We get here if we have a "#macro" construct // without parenthesis which is a parse error - throw new ParseException("A macro declaration requires at least a name argument", t); + throwParseException("A macro declaration requires at least a name argument"); } /**