diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index f02ed633fe..399f7809ed 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -276,168 +276,6 @@ class JavaParser { throw new ParseException("Line " + line + ", Column " + col + ": " + message); } - private void checkForBadAssertUsage(String in, String usage) { - if (jdkVersion > 3 && in.equals("assert")) { - throwParseException("Can't use 'assert' as " + usage + " when running in JDK 1.4 mode!"); - } - } - - private void checkForBadStaticImportUsage() { - if (jdkVersion < 5) { - throwParseException("Can't use static imports when running in JDK 1.4 mode!"); - } - } - - private void checkForBadAnnotationUsage() { - if (jdkVersion < 5) { - throwParseException("Can't use annotations when running in JDK 1.4 mode!"); - } - } - - private void checkForBadGenericsUsage() { - if (jdkVersion < 5) { - throwParseException("Can't use generics unless running in JDK 1.5 mode!"); - } - } - - private void checkForBadVariableArgumentsUsage() { - if (jdkVersion < 5) { - throwParseException("Can't use variable arguments (varargs) when running in JDK 1.4 mode!"); - } - } - - private void checkForBadJDK15ForLoopSyntaxArgumentsUsage() { - if (jdkVersion < 5) { - throwParseException("Can't use JDK 1.5 for loop syntax when running in JDK 1.4 mode!"); - } - } - - private void checkForBadEnumUsage(String in, String usage) { - if (jdkVersion >= 5 && in.equals("enum")) { - throwParseException("Can't use 'enum' as " + usage + " when running in JDK 1.5 mode!"); - } - } - - private void checkForBadHexFloatingPointLiteral() { - if (jdkVersion < 5) { - throwParseException("Can't use hexadecimal floating point literals in pre-JDK 1.5 target"); - } - } - - private void checkForBadNumericalLiteralslUsage(Token token) { - if (jdkVersion < 7) { - if (token.image.contains("_")) { - throwParseException("Can't use underscores in numerical literals when running in JDK inferior to 1.7 mode!"); - } - - if (token.image.startsWith("0b") || token.image.startsWith("0B")) { - throwParseException("Can't use binary numerical literals when running in JDK inferior to 1.7 mode!"); - } - } - } - - private void checkForBadDiamondUsage() { - if (jdkVersion < 7) { - throwParseException("Cannot use the diamond generic notation when running in JDK inferior to 1.7 mode!"); - } - } - - private void checkForBadTryWithResourcesUsage() { - if (jdkVersion < 7) { - throwParseException("Cannot use the try-with-resources notation when running in JDK inferior to 1.7 mode!"); - } - } - - private void checkForBadMultipleExceptionsCatching() { - if (jdkVersion < 7) { - throwParseException("Cannot catch multiple exceptions when running in JDK inferior to 1.7 mode!"); - } - } - - private void checkForBadLambdaUsage() { - if (jdkVersion < 8) { - throwParseException("Cannot use lambda expressions when running in JDK inferior to 1.8 mode!"); - } - } - private void checkForBadMethodReferenceUsage() { - if (jdkVersion < 8) { - throwParseException("Cannot use method references when running in JDK inferior to 1.8 mode!"); - } - } - private void checkForBadDefaultImplementationUsage() { - if (jdkVersion < 8) { - throwParseException("Cannot use default implementations in interfaces when running in JDK inferior to 1.8 mode!"); - } - } - private void checkForBadIntersectionTypesInCasts() { - if (jdkVersion < 8) { - throwParseException("Cannot use intersection types in casts when running in JDK inferior to 1.8 mode!"); - } - } - private void checkForBadTypeAnnotations() { - if (jdkVersion < 8) { - throwParseException("Cannot use type annotations when running in JDK inferior to 1.8 mode!"); - } - } - private void checkforBadExplicitReceiverParameter() { - if (jdkVersion < 8) { - throwParseException("Cannot use explicit receiver parameters when running in JDK inferior to 1.8 mode!"); - } - } - private void checkForBadAnonymousDiamondUsage() { - if (jdkVersion < 9) { - Node node = jjtree.peekNode(); - if (node instanceof ASTConstructorCall) { - ASTConstructorCall expr = (ASTConstructorCall) node; - ASTTypeArguments types = expr.getTypeNode().getTypeArguments(); - if (expr.isAnonymousClass() && types != null && types.isDiamond()) { - throwParseException("Cannot use '<>' with anonymous inner classes when running in JDK inferior to 9 mode!"); - } - } - } - } - /** - * Keeps track whether we are dealing with an interface or not. Needed since the tree is - * is not fully constructed yet, when we check for private interface methods. - * The flag is updated, if entering ClassOrInterfaceDeclaration and reset when leaving. - * The flag is also reset, if entering a anonymous inner class or enums. - */ - private boolean inInterface = false; - private void checkForBadPrivateInterfaceMethod(ASTMethodDeclaration node) { - if (jdkVersion < 9 && inInterface && node.isPrivate()) { - throwParseException("Cannot use private interface methods when running in JDK inferior to 9 mode!"); - } - } - private void checkForBadIdentifier(String image) { - if (jdkVersion >= 9 && "_".equals(image)) { - throwParseException("With JDK 9, '_' is a keyword, and may not be used as an identifier!"); - } - } - private void checkForBadModuleUsage() { - if (jdkVersion < 9) { - throwParseException("Cannot use module declaration when running in JDK inferior to 9 mode!"); - } - } - private void checkForBadConciseTryWithResourcesUsage() { - Node top = jjtree.peekNode(); - if (!(top instanceof ASTFieldAccess || top instanceof ASTVariableAccess)) { - throwParseException("Expected a variable access, but was a " + top.getXPathNodeName()); - } - - if (jdkVersion < 9) { - throwParseException("Cannot use concise try-with-resources when running in JDK inferior to 9 mode!"); - } - } - private void checkForBadTypeIdentifierUsage(String image) { - if (jdkVersion >= 10 && "var".equals(image)) { - throwParseException("With JDK 10, 'var' is a restricted local variable type and cannot be used for type declarations!"); - } - } - private void checkForMultipleCaseLabels() { - if ((jdkVersion != 12 && jdkVersion != 13) || !preview) { - throwParseException("Multiple case labels in switch statements are only supported with Java 12 or 13 Preview"); - } - } /** * Keeps track during tree construction, whether we are currently building a switch label. * A switch label must not contain a LambdaExpression. @@ -447,34 +285,6 @@ class JavaParser { * See also comment at #Expression(). */ private boolean inSwitchLabel = false; - private void checkForSwitchRules() { - if ((jdkVersion != 12 && jdkVersion != 13) || !preview) { - throwParseException("Switch rules in switch statements are only supported with Java 12 or 13 Preview"); - } - } - private void checkForSwitchExpression() { - if ((jdkVersion != 12 && jdkVersion != 13) || !preview) { - throwParseException("Switch expressions are only supported with Java 12 or 13 Preview"); - } - } - - private void checkForBreakExpression() { - if (jdkVersion != 12 || !preview) { - throwParseException("Expressions in break statements are only supported with Java 12 Preview"); - } - } - - private void checkForYieldStatement() { - if (jdkVersion != 13 || !preview) { - throwParseException("Yield statements are only supported with Java 13 Preview"); - } - } - - private void checkForTextBlockLiteral() { - if (jdkVersion != 13 || !preview) { - throwParseException("Text block literals are only supported with Java 13 Preview"); - } - } // This is a semantic LOOKAHEAD to determine if we're dealing with an assert @@ -1770,7 +1580,7 @@ void PackageDeclaration() : void ImportDeclaration() : {String image;} { - "import" [ "static" {checkForBadStaticImportUsage();jjtThis.setStatic();} ] + "import" [ "static" {jjtThis.setStatic();} ] image=VoidName() { jjtThis.setImage(image); } [ "." "*" {jjtThis.setImportOnDemand();} ] ";" } @@ -1800,7 +1610,7 @@ int Modifiers() #void: | "transient" { modifiers |= AccessNode.TRANSIENT; } | "volatile" { modifiers |= AccessNode.VOLATILE; } | "strictfp" { modifiers |= AccessNode.STRICTFP; } - | "default" { modifiers |= AccessNode.DEFAULT; checkForBadDefaultImplementationUsage(); } + | "default" { modifiers |= AccessNode.DEFAULT; } | Annotation() {numAnnots++;} ) )* @@ -1832,17 +1642,14 @@ void ClassOrInterfaceDeclaration(int modifiers): { Token t = null; jjtThis.setModifiers(modifiers); - boolean inInterfaceOld = inInterface; - inInterface = false; } { - ( "class" | "interface" { jjtThis.setInterface(); inInterface = true; } ) - t= { checkForBadTypeIdentifierUsage(t.image); jjtThis.setImage(t.image); } + ( "class" | "interface" { jjtThis.setInterface(); } ) + t= { jjtThis.setImage(t.image); } [ TypeParameters() ] [ ExtendsList() ] [ ImplementsList() ] ClassOrInterfaceBody() - { inInterface = inInterfaceOld; } // always restore the flag after leaving the node } void ExtendsList(): @@ -1872,29 +1679,20 @@ jjtThis.setModifiers(modifiers); if (!"enum".equals(t.image)) { throw new ParseException("ERROR: expecting enum"); } - - if (jdkVersion < 5) { - throw new ParseException("ERROR: Can't use enum as a keyword in pre-JDK 1.5 target"); - } } - t= {checkForBadTypeIdentifierUsage(t.image); jjtThis.setImage(t.image);} + t= {jjtThis.setImage(t.image);} [ ImplementsList() ] EnumBody() } void EnumBody(): -{ - boolean inInterfaceOld = inInterface; - inInterface = false; -} +{} { "{" [ EnumConstant() ( LOOKAHEAD(2) "," EnumConstant() )* ] [ "," ] [ ";" ( ClassOrInterfaceBodyDeclaration() )* ] "}" - - { inInterface = inInterfaceOld; } // always restore the flag after leaving the node } void EnumConstant(): @@ -1906,20 +1704,14 @@ void EnumConstant(): void TypeParameters(): {} { - "<" {checkForBadGenericsUsage();} TypeParameter() ( "," TypeParameter() )* ">" + "<" TypeParameter() ( "," TypeParameter() )* ">" } void TypeParameter(): {Token t;} { AnnotationList() - t= {jjtThis.setImage(t.image);} [ TypeBound() ] -} - -void TypeBound() #void: -{} -{ - "extends" IntersectionType(false) + t= {jjtThis.setImage(t.image);} [ "extends" IntersectionType() ] } void ClassOrInterfaceBody(): @@ -1960,17 +1752,9 @@ void VariableDeclarator() : // TODO use ArrayDimensions void VariableDeclaratorId() : +{} { - String image; -} -{ - { image = getToken(0).getImage(); } ( "[" "]" { jjtThis.bumpArrayDepth(); })* - { - checkForBadAssertUsage(image, "a variable name"); - checkForBadEnumUsage(image, "a variable name"); - checkForBadIdentifier(image); - jjtThis.setImage(image); - } + { setLastTokenImage(jjtThis); } ( "[" "]" { jjtThis.bumpArrayDepth(); })* } void ReceiverParameter(): @@ -1995,17 +1779,11 @@ void ArrayInitializer() : void MethodDeclaration(int modifiers) : { jjtThis.setModifiers(modifiers); - checkForBadPrivateInterfaceMethod(jjtThis); } { [ TypeParameters() ] ResultType() - { - String image = getToken(0).getImage(); - jjtThis.setImage(image); - checkForBadAssertUsage(image, "a method name"); - checkForBadEnumUsage(image, "a method name"); - } + { setLastTokenImage(jjtThis); } FormalParameters() ( "[" "]" )* // TODO use ArrayDimensions [ ThrowsList() ] @@ -2024,11 +1802,11 @@ void FormalParameter() : } { ( "final" {jjtThis.setFinal(true);} | Annotation() )* - Type() ("|" {checkForBadMultipleExceptionsCatching();} Type())* + Type() ("|" Type())* // TODO there may be annotations before the "..." of the varargs // the ... is treated analogously to a pair of brackets // the sensible way to parse it would probably be to push an ArrayType with ArrayTypeDim for the "..." - [ "..." {checkForBadVariableArgumentsUsage(); jjtThis.setVarargs();} ] + [ "..." {jjtThis.setVarargs();} ] VariableDeclaratorId() } @@ -2100,10 +1878,10 @@ Dims: */ -void IntersectionType(boolean inCast) #IntersectionType(isIntersection): +void IntersectionType() #IntersectionType(isIntersection): {boolean isIntersection=false;} { - AnnotatedType() ( "&" {if (inCast) checkForBadIntersectionTypesInCasts();isIntersection=true;} AnnotatedClassOrInterfaceType() )* + AnnotatedType() ( "&" {isIntersection=true;} AnnotatedClassOrInterfaceType() )* } void AnnotationList() #void: @@ -2268,9 +2046,9 @@ void TypeArguments(): {} { LOOKAHEAD(2) - "<" {checkForBadGenericsUsage();} TypeArgument() ( "," TypeArgument() )* ">" + "<" TypeArgument() ( "," TypeArgument() )* ">" | - "<" {checkForBadDiamondUsage();} ">" + "<" ">" } void TypeArgument() #void: @@ -2529,8 +2307,8 @@ void UnaryExpressionNotPlusMinus() #void: | LOOKAHEAD("(" TypeAnnotationList() PrimitiveType() ")") ("(" AnnotatedType() ")" UnaryExpression()) #CastExpression // here we avoid looking ahead for a whole unary expression, instead just testing the token after ")" -| LOOKAHEAD("(" IntersectionType(true) ")" UnaryExprNotPmStart() ) - ("(" IntersectionType(true) ")" UnaryExpressionNotPlusMinus()) #CastExpression +| LOOKAHEAD("(" IntersectionType() ")" UnaryExprNotPmStart() ) + ("(" IntersectionType() ")" UnaryExpressionNotPlusMinus()) #CastExpression | PostfixExpression() | SwitchExpression() } @@ -2557,7 +2335,6 @@ void PostfixExpression() #void: void SwitchExpression() : {} { - {checkForSwitchExpression();} "switch" "(" Expression() ")" SwitchBlock() } @@ -2702,7 +2479,7 @@ void MemberSelector() #void : } void MethodReference(): // LHS is injected -{checkForBadMethodReferenceUsage();} +{} { "::" {jjtree.extendLeft();} [TypeArguments()] @@ -2729,7 +2506,7 @@ private void LambdaLahead() #void : // To prevent LambdaExpressions in switch labels, the field #inSwitchLabel is used // as a workaround. void LambdaExpression(): -{ checkForBadLambdaUsage(); } +{ } { LambdaParameterList() "->" ( Expression() | Block() ) } @@ -2753,7 +2530,7 @@ void LambdaParameter(): [ isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} isVarType=LambdaParameterType() { jjtThis.setVarType(); } - [ "..." {checkForBadVariableArgumentsUsage(); jjtThis.setVarargs();} ] + [ "..." {jjtThis.setVarargs();} ] ] VariableDeclaratorId() } @@ -2781,14 +2558,11 @@ void Literal() #void : void NumericLiteral(): { Token t; } { - ( t= { jjtThis.setIntLiteral();} - | t= { jjtThis.setFloatLiteral();} - | t= { checkForBadHexFloatingPointLiteral(); jjtThis.setFloatLiteral();} + ( t= { jjtThis.setIntLiteral(); } + | t= { jjtThis.setFloatLiteral(); } + | t= { jjtThis.setFloatLiteral(); } ) - { - checkForBadNumericalLiteralslUsage(t); - jjtThis.setImage(t.image); - } + { jjtThis.setImage(t.image); } } void CharLiteral(): @@ -2801,7 +2575,7 @@ void StringLiteral(): {} { ( - | { jjtThis.setTextBlock(); checkForTextBlockLiteral(); }) + | { jjtThis.setTextBlock(); }) { jjtThis.setImage(getToken(0).getImage()); } } @@ -2821,12 +2595,7 @@ void QualifiedAllocationExpr() #ConstructorCall: [ TypeArguments() ] AnnotatedClassOrInterfaceType() ArgumentList() - [ - { boolean inInterfaceOld = inInterface; inInterface = false; /* a anonymous class is not a interface */ } - ClassOrInterfaceBody() #AnonymousClassDeclaration - { inInterface = inInterfaceOld; } // always restore the flag after leaving the node - ] - { checkForBadAnonymousDiamondUsage(); } + [ ClassOrInterfaceBody() #AnonymousClassDeclaration ] } @@ -2850,12 +2619,7 @@ void UnqualifiedAllocationExpr() #void : ( ArrayDimsAndInits() {isArrayInit=true;} | - ArgumentList() - [ - { boolean inInterfaceOld = inInterface; inInterface = false; /* a anonymous class is not a interface */ } - ClassOrInterfaceBody() #AnonymousClassDeclaration - { inInterface = inInterfaceOld; } // always restore the flag after leaving the node - ] + ArgumentList() [ ClassOrInterfaceBody() #AnonymousClassDeclaration ] ) ) {/*Empty unit, important*/} @@ -2863,7 +2627,6 @@ void UnqualifiedAllocationExpr() #void : #ArrayAllocation(isArrayInit) ) #ConstructorCall(!isArrayInit) - { checkForBadAnonymousDiamondUsage(); } } @@ -3030,13 +2793,13 @@ void SwitchBlock() #void : } void SwitchLabeledRule() #void : -{checkForSwitchRules();} +{} { SwitchLabel() "->" SwitchLabeledRulePart() } void SwitchLabeledRulePart() #void: -{checkForSwitchRules();} +{} { ( Expression() ";" ) #SwitchLabeledExpression(2) | @@ -3060,7 +2823,7 @@ void SwitchLabel() : { { inSwitchLabel = true; } ( - "case" ConditionalExpression() ({checkForMultipleCaseLabels();} "," ConditionalExpression() )* + "case" ConditionalExpression() ( "," ConditionalExpression() )* | "default" {jjtThis.setDefault();} ) @@ -3068,7 +2831,7 @@ void SwitchLabel() : } void YieldStatement() : -{ checkForYieldStatement(); } +{ } { Expression() ";" } @@ -3102,7 +2865,6 @@ void ForStatement() : "for" "(" ( LOOKAHEAD(LocalVariableDeclaration() ":") - {checkForBadJDK15ForLoopSyntaxArgumentsUsage();} LocalVariableDeclaration() ":" Expression() | [ ForInit() ] ";" @@ -3136,7 +2898,7 @@ void ForUpdate() : void BreakStatement() : {Token t;} { - "break" [ LOOKAHEAD( ";") t= {jjtThis.setImage(t.image);} | Expression() {checkForBreakExpression();} ] ";" + "break" [ LOOKAHEAD( ";") t= {jjtThis.setImage(t.image);} | Expression() ] ";" } void ContinueStatement() : @@ -3178,7 +2940,6 @@ void TryStatement() : void ResourceList(): {} { - {checkForBadTryWithResourcesUsage();} "(" Resource() (LOOKAHEAD(2) ";" Resource())* (";" {jjtThis.setTrailingSemi();})? @@ -3194,7 +2955,13 @@ void Resource() : LocalVariableType() VariableDeclarator() ) #LocalVariableDeclaration | - PrimaryExpression() {checkForBadConciseTryWithResourcesUsage();} {} + PrimaryExpression() + { + Node top = jjtree.peekNode(); + if (!(top instanceof ASTVariableAccess || top instanceof ASTFieldAccess)) + throwParseException("Expected a variable access, but was a " + top.getXPathNodeName()); + } + {} } void CatchStatement() : @@ -3212,11 +2979,7 @@ void FinallyStatement() : } void AssertStatement() : -{ - if (jdkVersion <= 3) { - throw new ParseException("Can't use 'assert' as a keyword when running in JDK 1.3 mode!"); - } -} +{} { Expression() [ ":" Expression() ] ";" } @@ -3248,16 +3011,13 @@ void RSIGNEDSHIFT() #void: void Annotation() #void: {} { - ( - LOOKAHEAD( "@" VoidName() "(" ( "=" | ")" )) - NormalAnnotation() - | - LOOKAHEAD( "@" VoidName() "(" ) - SingleMemberAnnotation() - | - MarkerAnnotation() - ) - {checkForBadAnnotationUsage();} + LOOKAHEAD( "@" VoidName() "(" ( "=" | ")" )) + NormalAnnotation() +| + LOOKAHEAD( "@" VoidName() "(" ) + SingleMemberAnnotation() +| + MarkerAnnotation() } void AnnotationBase(Node n) #void: @@ -3319,7 +3079,7 @@ void MemberValueArrayInitializer(): void TypeAnnotation() #void: {} { - Annotation() {checkForBadTypeAnnotations();} + Annotation() } @@ -3327,16 +3087,10 @@ void TypeAnnotation() #void: void AnnotationTypeDeclaration(int modifiers): { -Token t; jjtThis.setModifiers(modifiers); } { - "@" "interface" t= - { - checkForBadAnnotationUsage(); - checkForBadTypeIdentifierUsage(t.image); - jjtThis.setImage(t.image); - } + "@" "interface" { setLastTokenImage(jjtThis); } AnnotationTypeBody() } @@ -3393,7 +3147,6 @@ void ModuleDeclaration(): { StringBuilder s = new StringBuilder(); Token t; - checkForBadModuleUsage(); } { ( Annotation() )* [LOOKAHEAD({isKeyword("open")}) {jjtThis.setOpen(true);}] LOOKAHEAD({isKeyword("module")}) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java index aa04abdf6d..6d484afeef 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java @@ -18,6 +18,11 @@ import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; */ public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, AccessNode, JavaNode { + + default String getSimpleName() { + return getImage(); + } + /** * Finds the type kind of this declaration. * diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorCall.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorCall.java index 0375727e12..08136f8e4f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorCall.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorCall.java @@ -80,6 +80,12 @@ public final class ASTConstructorCall extends AbstractJavaExpr implements ASTPri return (ASTArgumentList) jjtGetChild(idx); } + /** Returns true if type arguments to the constructed instance's type are inferred. */ + public boolean usesDiamondTypeArgs() { + ASTTypeArguments targs = getTypeNode().getTypeArguments(); + return targs != null && targs.isDiamond(); + } + /** * Returns the type node. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java index e176e55ad8..c113e09457 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNumericLiteral.java @@ -130,7 +130,19 @@ public final class ASTNumericLiteral extends AbstractLiteral implements ASTLiter return getImage().replaceAll("_++", ""); } - private int getIntBase() { + /** + * Returns true if this is an integral literal, ie either a long or + * an integer literal. Otherwise, this is a floating point literal. + */ + public boolean isIntegral() { + return isIntegral; + } + + /** + * Returns the base of the literal, eg 8 for an octal literal, + * 10 for a decimal literal, etc. + */ + public int getBase() { final String image = getImage().toLowerCase(Locale.ROOT); if (image.startsWith("0x")) { return 16; @@ -161,7 +173,7 @@ public final class ASTNumericLiteral extends AbstractLiteral implements ASTLiter public long getValueAsLong() { if (isIntegral) { // Using BigInteger to allow parsing 0x8000000000000000+ numbers as negative instead of a NumberFormatException - BigInteger bigInt = new BigInteger(stripIntValue(), getIntBase()); + BigInteger bigInt = new BigInteger(stripIntValue(), getBase()); return bigInt.longValue(); } else { return (long) getValueAsDouble(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java index 07862989ac..f62eef5f5b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java @@ -4,8 +4,13 @@ package net.sourceforge.pmd.lang.java.ast; +import java.util.Collections; import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.internal.util.IteratorUtil; + /** * Try statement node. @@ -51,6 +56,16 @@ public final class ASTTryStatement extends AbstractJavaNode { return jjtGetChild(0) instanceof ASTResourceList; } + @Nullable + public ASTResourceList getResourceList() { + return AstImplUtil.getChildAs(this, 0, ASTResourceList.class); + } + + public List getResources() { + ASTResourceList list = getResourceList(); + return list == null ? Collections.emptyList() : IteratorUtil.toList(list.iterator()); + } + /** * Returns the catch statement nodes of this try statement. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java index 374c755c67..09632fd5e6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java @@ -10,6 +10,7 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.AbstractTokenManager; import net.sourceforge.pmd.lang.ast.JavaCharStream; +import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; @@ -63,18 +64,19 @@ public final class InternalApiBridge { } } - public static ASTCompilationUnit parseInternal(String fileName, Reader source, int jdkVersion, boolean preview, ParserOptions options) { + public static ASTCompilationUnit parseInternal(String fileName, Reader source, LanguageLevelChecker checker, ParserOptions options) { JavaParser parser = new JavaParser(new JavaCharStream(source)); String suppressMarker = options.getSuppressMarker(); if (suppressMarker != null) { parser.setSuppressMarker(suppressMarker); } - parser.setJdkVersion(jdkVersion); - parser.setPreview(preview); + parser.setJdkVersion(checker.getJdkVersion()); + parser.setPreview(checker.isPreviewEnabled()); AbstractTokenManager.setFileName(fileName); ASTCompilationUnit acu = parser.CompilationUnit(); acu.setNoPmdComments(parser.getSuppressMap()); + checker.check(acu); return acu; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java index 4d770d4794..4498bc3d82 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java @@ -13,4 +13,35 @@ package net.sourceforge.pmd.lang.java.ast; * @since 7.0.0 */ public class SideEffectingVisitorAdapter implements SideEffectingVisitor { + + + public void visit(ASTAnnotation node, T data) { + visit((JavaNode) node, data); + } + + @Override + public void visit(ASTSingleMemberAnnotation node, T data) { + visit((ASTAnnotation) node, data); + } + + @Override + public void visit(ASTNormalAnnotation node, T data) { + visit((ASTAnnotation) node, data); + } + + @Override + public void visit(ASTMarkerAnnotation node, T data) { + visit((ASTAnnotation) node, data); + } + + // TODO delegation + + + public void visit(ASTSwitchLabeledRule node, T data) { + visit((JavaNode) node, data); + } + + public void visit(ASTAnyTypeDeclaration node, T data) { + visit((JavaNode) node, data); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java new file mode 100644 index 0000000000..e0732c884f --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java @@ -0,0 +1,457 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast.internal; + + +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; + +import net.sourceforge.pmd.internal.util.IteratorUtil; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAssertStatement; +import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; +import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; +import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTIntersectionType; +import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; +import net.sourceforge.pmd.lang.java.ast.ASTModuleDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTReceiverParameter; +import net.sourceforge.pmd.lang.java.ast.ASTResource; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchExpression; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel; +import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabeledRule; +import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; +import net.sourceforge.pmd.lang.java.ast.ASTType; +import net.sourceforge.pmd.lang.java.ast.ASTTypeArguments; +import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.ASTYieldStatement; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.SideEffectingVisitorAdapter; + +/** + * Checks that an AST conforms to some language level. The reporting + * behaviour is parameterized with a {@link ReportingStrategy}. + * + * @param Type of object accumulating violations + */ +public class LanguageLevelChecker { + + private final int jdkVersion; + private final boolean preview; + private final CheckVisitor visitor = new CheckVisitor(); + private final ReportingStrategy reportingStrategy; + + public LanguageLevelChecker(int jdkVersion, boolean preview, ReportingStrategy reportingStrategy) { + this.jdkVersion = jdkVersion; + this.preview = preview; + this.reportingStrategy = reportingStrategy; + } + + public int getJdkVersion() { + return jdkVersion; + } + + public boolean isPreviewEnabled() { + return preview; + } + + + public void check(JavaNode node) { + T accumulator = reportingStrategy.createAccumulator(); + node.jjtAccept(visitor, accumulator); + reportingStrategy.done(accumulator); + } + + private boolean check(Node node, LanguageFeature message, T acc) { + if (message.isAvailable(this.jdkVersion, this.preview)) { + return true; + } + + reportingStrategy.report(node, message.whenUnavailableMessage(), acc); + return false; + } + + + private class CheckVisitor extends SideEffectingVisitorAdapter { + + @Override + public void visit(ASTStringLiteral node, T data) { + if (jdkVersion != 13 || !preview) { + if (node.isTextBlock()) { + check(node, PreviewFeature.TEXT_BLOCK_LITERALS, data); + } + } + visitChildren(node, data); + } + + @Override + public void visit(ASTImportDeclaration node, T data) { + if (node.isStatic()) { + check(node, RegularLanguageFeature.STATIC_IMPORT, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTYieldStatement node, T data) { + check(node, PreviewFeature.YIELD_STATEMENTS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTBreakStatement node, T data) { + if (node.jjtGetNumChildren() > 0) { + check(node, PreviewFeature.BREAK__WITH__VALUE_STATEMENTS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTSwitchExpression node, T data) { + check(node, PreviewFeature.SWITCH_EXPRESSIONS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTConstructorCall node, T data) { + if (node.usesDiamondTypeArgs()) { + if (check(node, RegularLanguageFeature.DIAMOND_TYPE_ARGUMENTS, data) && node.isAnonymousClass()) { + check(node, RegularLanguageFeature.DIAMOND_TYPE_ARGUMENTS_FOR_ANONYMOUS_CLASSES, data); + } + } + visitChildren(node, data); + } + + @Override + public void visit(ASTTypeArguments node, T data) { + check(node, RegularLanguageFeature.GENERICS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTTypeParameters node, T data) { + check(node, RegularLanguageFeature.GENERICS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTFormalParameter node, T data) { + if (node.isVarargs()) { + check(node, RegularLanguageFeature.VARARGS_PARAMETERS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTReceiverParameter node, T data) { + check(node, RegularLanguageFeature.RECEIVER_PARAMETERS, data); + } + + @Override + public void visit(ASTAnnotation node, T data) { + if (node.jjtGetParent() instanceof ASTType) { + check(node, RegularLanguageFeature.TYPE_ANNOTATIONS, data); + } else { + check(node, RegularLanguageFeature.ANNOTATIONS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTForStatement node, T data) { + if (node.isForeach()) { + check(node, RegularLanguageFeature.FOREACH_LOOPS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTEnumDeclaration node, T data) { + check(node, RegularLanguageFeature.ENUMS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTNumericLiteral node, T data) { + int base = node.getBase(); + if (base == 16 && !node.isIntegral()) { + check(node, RegularLanguageFeature.HEXADECIMAL_FLOATING_POINT_LITERALS, data); + } else if (base == 2) { + check(node, RegularLanguageFeature.BINARY_NUMERIC_LITERALS, data); + } else if (node.getImage().indexOf('_') >= 0) { + check(node, RegularLanguageFeature.UNDERSCORES_IN_NUMERIC_LITERALS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTMethodReference node, T data) { + check(node, RegularLanguageFeature.METHOD_REFERENCES, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTLambdaExpression node, T data) { + check(node, RegularLanguageFeature.LAMBDA_EXPRESSIONS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTMethodDeclaration node, T data) { + if (node.isDefault()) { + check(node, RegularLanguageFeature.DEFAULT_METHODS, data); + } + + if (node.isPrivate() && node.isInterfaceMember()) { + check(node, RegularLanguageFeature.PRIVATE_METHODS_IN_INTERFACES, data); + } + + checkIdent(node, node.getMethodName(), data); + visitChildren(node, data); + } + + @Override + public void visit(ASTAssertStatement node, T data) { + check(node, RegularLanguageFeature.ASSERT_STATEMENTS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTTryStatement node, T data) { + if (node.isTryWithResources()) { + if (check(node, RegularLanguageFeature.TRY_WITH_RESOURCES, data)) { + for (ASTResource resource : node.getResources()) { + if (resource.isConciseResource()) { + check(node, RegularLanguageFeature.CONCISE_RESOURCE_SYNTAX, data); + break; + } + } + } + } + visitChildren(node, data); + } + + @Override + public void visit(ASTIntersectionType node, T data) { + if (node.jjtGetParent() instanceof ASTCastExpression) { + check(node, RegularLanguageFeature.INTERSECTION_TYPES_IN_CASTS, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTCatchStatement node, T data) { + if (node.isMulticatchStatement()) { + check(node, RegularLanguageFeature.COMPOSITE_CATCH_CLAUSES, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTSwitchLabel node, T data) { + if (IteratorUtil.count(node.iterator()) > 1) { + check(node, PreviewFeature.COMPOSITE_CASE_LABEL, data); + } + visitChildren(node, data); + } + + @Override + public void visit(ASTModuleDeclaration node, T data) { + check(node, RegularLanguageFeature.MODULE_DECLARATIONS, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTSwitchLabeledRule node, T data) { + check(node, PreviewFeature.SWITCH_RULES, data); + visitChildren(node, data); + } + + @Override + public void visit(ASTVariableDeclaratorId node, T data) { + checkIdent(node, node.getVariableName(), data); + visitChildren(node, data); + } + + @Override + public void visit(ASTAnyTypeDeclaration node, T data) { + checkIdent(node, node.getSimpleName(), data); + visitChildren(node, data); + } + + private void visitChildren(JavaNode node, T data) { + for (int i = 0; i < node.jjtGetNumChildren(); i++) { + node.jjtGetChild(i).jjtAccept(visitor, data); + } + } + + private void checkIdent(JavaNode node, String simpleName, T acc) { + if ("var".equals(simpleName)) { + check(node, ReservedIdentifiers.VAR_AS_A_TYPE_NAME, acc); + } else if ("enum".equals(simpleName)) { + check(node, ReservedIdentifiers.ENUM_AS_AN_IDENTIFIER, acc); + } else if ("assert".equals(simpleName)) { + check(node, ReservedIdentifiers.ASSERT_AS_AN_IDENTIFIER, acc); + } else if ("_".equals(simpleName)) { + check(node, ReservedIdentifiers.UNDERSCORE_AS_AN_IDENTIFIER, acc); + } + } + + } + + private static String displayNameLower(String name) { + return name.replaceAll("__", "-") + .replace('_', ' ') + .toLowerCase(Locale.ROOT); + } + + + private static String versionDisplayName(int jdk) { + if (jdk < 8) { + return "Java 1." + jdk; + } else { + return "Java " + jdk; + } + } + + /** Those are hacked just for the preview features. */ + private enum PreviewFeature implements LanguageFeature { + BREAK__WITH__VALUE_STATEMENTS(12, false), + + COMPOSITE_CASE_LABEL(12, true), + SWITCH_EXPRESSIONS(12, true), + SWITCH_RULES(12, true), + + TEXT_BLOCK_LITERALS(13, false), + YIELD_STATEMENTS(13, false); + + + private final int minJdkVersion; + private final boolean alsoAbove; + + PreviewFeature(int minJdkVersion, boolean alsoAbove) { + this.minJdkVersion = minJdkVersion; + this.alsoAbove = alsoAbove; + } + + @Override + public boolean isAvailable(int jdk, boolean preview) { + return preview && (jdk == minJdkVersion || alsoAbove && jdk > minJdkVersion); + } + + + @Override + public String whenUnavailableMessage() { + return StringUtils.capitalize(displayNameLower(name())) + + " is a feature of JDK >= " + "Java " + minJdkVersion + " preview" + + ", you should select your language version accordingly"; + } + } + + /** Those use a max valid version. */ + private enum ReservedIdentifiers implements LanguageFeature { + ASSERT_AS_AN_IDENTIFIER(4, "assert"), + ENUM_AS_AN_IDENTIFIER(5, "enum"), + UNDERSCORE_AS_AN_IDENTIFIER(9, "_"), + VAR_AS_A_TYPE_NAME(10, "var"); + + private final int maxJdkVersion; + private final String reserved; + + ReservedIdentifiers(int minJdkVersion, String reserved) { + this.maxJdkVersion = minJdkVersion; + this.reserved = reserved; + } + + @Override + public boolean isAvailable(int jdk, boolean preview) { + return jdk < this.maxJdkVersion; + } + + @Override + public String whenUnavailableMessage() { + String s = displayNameLower(name()); + String usageType = s.substring(s.indexOf(' ') + 1); // eg "as an identifier" + return "Since " + LanguageLevelChecker.versionDisplayName(maxJdkVersion) + ", '" + reserved + "'" + + " is reserved and cannot be used " + usageType; + } + } + + /** Those use a min valid version. */ + private enum RegularLanguageFeature implements LanguageFeature { + + ASSERT_STATEMENTS(4), + + STATIC_IMPORT(5), + ENUMS(5), + GENERICS(5), + ANNOTATIONS(5), + FOREACH_LOOPS(5), + VARARGS_PARAMETERS(5), + HEXADECIMAL_FLOATING_POINT_LITERALS(5), + + UNDERSCORES_IN_NUMERIC_LITERALS(7), + BINARY_NUMERIC_LITERALS(7), + TRY_WITH_RESOURCES(7), + COMPOSITE_CATCH_CLAUSES(7), + DIAMOND_TYPE_ARGUMENTS(7), + + DEFAULT_METHODS(8), + RECEIVER_PARAMETERS(8), + TYPE_ANNOTATIONS(8), + INTERSECTION_TYPES_IN_CASTS(8), + LAMBDA_EXPRESSIONS(8), + METHOD_REFERENCES(8), + + MODULE_DECLARATIONS(9), + DIAMOND_TYPE_ARGUMENTS_FOR_ANONYMOUS_CLASSES(9), + PRIVATE_METHODS_IN_INTERFACES(9), + CONCISE_RESOURCE_SYNTAX(9); + + private final int minJdkLevel; + + RegularLanguageFeature(int minJdkLevel) { + this.minJdkLevel = minJdkLevel; + } + + + @Override + public boolean isAvailable(int jdk, boolean preview) { + return jdk >= this.minJdkLevel; + } + + + @Override + public String whenUnavailableMessage() { + return StringUtils.capitalize(displayNameLower(name())) + + " are a feature of " + versionDisplayName(minJdkLevel) + + ", you should select your language version accordingly"; + } + + } + + interface LanguageFeature { + + boolean isAvailable(int jdk, boolean preview); + + + String whenUnavailableMessage(); + } + + +} 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 new file mode 100644 index 0000000000..0871b803c8 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/ReportingStrategy.java @@ -0,0 +1,61 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast.internal; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ParseException; + +/** + * Strategy for reporting language-feature violations, for use by a + * {@link LanguageLevelChecker}. For example, {@link ReportingStrategy#reporterThatThrows()} + * produces a checker that throws a parse exception. It would be trivial + * to make eg a checker that eg collects all warnings instead of failing + * on the first one. + * + * @param Type of object accumulating violations + */ +public interface ReportingStrategy { + + /** Create a blank accumulator before performing the check. */ + T createAccumulator(); + + + /** Consume the accumulator, after all violations have been reported. */ + void done(T accumulator); + + + /** + * Report that a node violates a language feature. This doesn't have + * to throw an exception, we could also just warn, or accumulate into + * the parameter. + */ + void report(Node node, String message, T acc); + + + /** + * Creates a reporter that throws a {@link ParseException} when the + * first error is reported. + */ + static ReportingStrategy reporterThatThrows() { + return new ReportingStrategy() { + @Override + public Void createAccumulator() { + return null; + } + + @Override + public void done(Void accumulator) { + // do nothing + } + + @Override + public void report(Node node, String message, Void acc) { + throw new ParseException( + "Line " + node.getBeginLine() + ", Column " + node.getBeginColumn() + ": " + message); + } + }; + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index d0c66e3129..cb28bdf410 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -21,6 +21,8 @@ import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; +import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; +import net.sourceforge.pmd.lang.java.ast.internal.ReportingStrategy; import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade; import net.sourceforge.pmd.lang.java.dfa.JavaDFAGraphRule; import net.sourceforge.pmd.lang.java.metrics.JavaMetricsComputer; @@ -46,8 +48,7 @@ import net.sf.saxon.sxpath.IndependentContext; public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { - private final int jdkVersion; - private final boolean preview; + private final LanguageLevelChecker levelChecker; private final LanguageMetricsProvider myMetricsProvider = new JavaMetricsProvider(); public JavaLanguageHandler(int jdkVersion) { @@ -56,14 +57,13 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { public JavaLanguageHandler(int jdkVersion, boolean preview) { super(JavaProcessingStage.class); - this.jdkVersion = jdkVersion; - this.preview = preview; + this.levelChecker = new LanguageLevelChecker<>(jdkVersion, preview, ReportingStrategy.reporterThatThrows()); } @Override public Parser getParser(ParserOptions parserOptions) { - return new JavaLanguageParser(jdkVersion, preview, parserOptions); + return new JavaLanguageParser(levelChecker, parserOptions); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageParser.java index 41b84e2011..55817f8b3b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageParser.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageParser.java @@ -2,7 +2,6 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ - package net.sourceforge.pmd.lang.java.internal; import java.io.Reader; @@ -14,6 +13,7 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.JavaTokenManager; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.ParseException; +import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; /** * Adapter for the JavaParser, using the specified grammar version. @@ -23,13 +23,11 @@ import net.sourceforge.pmd.lang.java.ast.ParseException; */ public class JavaLanguageParser extends AbstractParser { - private final int jdkVersion; - private final boolean preview; + private final LanguageLevelChecker checker; - public JavaLanguageParser(int jdkVersion, boolean preview, ParserOptions parserOptions) { + JavaLanguageParser(LanguageLevelChecker checker, ParserOptions parserOptions) { super(parserOptions); - this.jdkVersion = jdkVersion; - this.preview = preview; + this.checker = checker; } @Override @@ -40,6 +38,6 @@ public class JavaLanguageParser extends AbstractParser { @Override public Node parse(String fileName, Reader source) throws ParseException { - return InternalApiBridge.parseInternal(fileName, source, jdkVersion, preview, getParserOptions()); + return InternalApiBridge.parseInternal(fileName, source, checker, getParserOptions()); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.java deleted file mode 100644 index e8641e8528..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes; - -import org.junit.Test; - -import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; - -public class ASTAnnotationTest { - - @Test - public void testAnnotationSucceedsWithDefaultMode() { - getNodes(ASTAnnotation.class, TEST1); - } - - @Test(expected = ParseException.class) - public void testAnnotationFailsWithJDK14() { - getNodes(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.4"), ASTAnnotation.class, TEST1); - } - - @Test - public void testAnnotationSucceedsWithJDK15() { - getNodes(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"), ASTAnnotation.class, TEST1); - } - - private static final String TEST1 = "public class Foo extends Buz {" + PMD.EOL + " @Override" + PMD.EOL - + " void bar() {" + PMD.EOL + " // overrides a superclass method" + PMD.EOL + " }" + PMD.EOL + "}"; -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java index 37d2d56d71..5accd2d88e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ParserCornersTest.java @@ -156,7 +156,7 @@ public class ParserCornersTest { fail("Expected exception"); } catch (ParseException e) { assertEquals( - "Line 1, Column 94: Cannot catch multiple exceptions when running in JDK inferior to 1.7 mode!", + "Line 1, Column 70: Composite catch clauses are a feature of Java 1.7, you should select your language version accordingly", e.getMessage()); } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt index b29facbe89..8efcf64aaa 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.java.ast import net.sourceforge.pmd.lang.ast.test.shouldBe +import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Earliest +import net.sourceforge.pmd.lang.java.ast.JavaVersion.J1_3 import net.sourceforge.pmd.lang.java.ast.ParserTestCtx.Companion.AnnotationParsingCtx /** @@ -13,6 +15,15 @@ import net.sourceforge.pmd.lang.java.ast.ParserTestCtx.Companion.AnnotationParsi */ class ASTAnnotationTest : ParserTestSpec({ + + parserTest("Test annot fails before JDK 1.4", javaVersions = Earliest..J1_3) { + + inContext(AnnotationParsingCtx) { + "@F" shouldNot parse() + "@F(a=1)" shouldNot parse() + } + } + parserTest("Marker annotations") { inContext(AnnotationParsingCtx) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt index af1722b1b1..a4ad3f9fcb 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchStatementTest.kt @@ -12,7 +12,7 @@ class ASTCatchStatementTest : ParserTestSpec({ parserTest("Test crash on multicatch", javaVersions = Earliest..J1_6) { - expectParseException("Cannot catch multiple exceptions when running in JDK inferior to 1.7 mode") { + expectParseException("Composite catch clauses are a feature of Java 1.7, you should select your language version accordingly") { parseAstStatement("try { } catch (IOException | AssertionError e) { }") }