diff options
Diffstat (limited to 'javaparser-core/src/main/java/com/github/javaparser/ast/validator')
28 files changed, 1086 insertions, 0 deletions
diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java10Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java10Validator.java new file mode 100644 index 000000000..c7fda2f46 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java10Validator.java @@ -0,0 +1,19 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.type.VarType; +import com.github.javaparser.ast.validator.chunks.VarValidator; + +/** + * This validator validates according to Java 10 syntax rules. + */ +public class Java10Validator extends Java9Validator { + + protected final Validator varOnlyOnLocalVariableDefinitionAndFor = new SingleNodeTypeValidator<>(VarType.class, new VarValidator(false)); + + public Java10Validator() { + super(); + add(varOnlyOnLocalVariableDefinitionAndFor); + /* There is no validator that validates that "var" is not used in Java 9 and lower, since the parser will never create a VarType node, + because that is done by the Java10 postprocessor. You can add it by hand, but that is obscure enough to ignore. */ + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java11Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java11Validator.java new file mode 100644 index 000000000..0eb879aa0 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java11Validator.java @@ -0,0 +1,16 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.type.VarType; +import com.github.javaparser.ast.validator.chunks.VarValidator; + +/** + * This validator validates according to Java 11 syntax rules. + */ +public class Java11Validator extends Java10Validator { + protected final Validator varAlsoInLambdaParameters = new SingleNodeTypeValidator<>(VarType.class, new VarValidator(true)); + + public Java11Validator() { + super(); + replace(varOnlyOnLocalVariableDefinitionAndFor, varAlsoInLambdaParameters); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_0Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_0Validator.java new file mode 100644 index 000000000..0d1051d67 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_0Validator.java @@ -0,0 +1,121 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.*; +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.ClassExpr; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; +import com.github.javaparser.ast.modules.ModuleDeclaration; +import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments; +import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters; +import com.github.javaparser.ast.stmt.AssertStmt; +import com.github.javaparser.ast.stmt.ForeachStmt; +import com.github.javaparser.ast.stmt.SwitchEntryStmt; +import com.github.javaparser.ast.stmt.TryStmt; +import com.github.javaparser.ast.type.UnionType; +import com.github.javaparser.ast.validator.chunks.CommonValidators; +import com.github.javaparser.ast.validator.chunks.ModifierValidator; +import com.github.javaparser.ast.validator.chunks.NoBinaryIntegerLiteralsValidator; +import com.github.javaparser.ast.validator.chunks.NoUnderscoresInIntegerLiteralsValidator; + +/** + * This validator validates according to Java 1.0 syntax rules. + */ +public class Java1_0Validator extends Validators { + protected final Validator modifiersWithoutStrictfpAndDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods + = new ModifierValidator(false, false, false); + protected final Validator noAssertKeyword = new SimpleValidator<>(AssertStmt.class, + n -> true, + (n, reporter) -> reporter.report(n, "'assert' keyword is not supported.") + ); + protected final Validator noInnerClasses = new SimpleValidator<>(ClassOrInterfaceDeclaration.class, + n -> !n.isTopLevelType(), + (n, reporter) -> reporter.report(n, "inner classes or interfaces are not supported.") + ); + protected final Validator noReflection = new SimpleValidator<>(ClassExpr.class, + n -> true, + (n, reporter) -> reporter.report(n, "Reflection is not supported.") + ); + protected final Validator noGenerics = new TreeVisitorValidator((node, reporter) -> { + if (node instanceof NodeWithTypeArguments) { + if (((NodeWithTypeArguments<? extends Node>) node).getTypeArguments().isPresent()) { + reporter.report(node, "Generics are not supported."); + } + } + if (node instanceof NodeWithTypeParameters) { + if (((NodeWithTypeParameters<? extends Node>) node).getTypeParameters().isNonEmpty()) { + reporter.report(node, "Generics are not supported."); + } + } + }); + protected final SingleNodeTypeValidator<TryStmt> tryWithoutResources = new SingleNodeTypeValidator<>(TryStmt.class, (n, reporter) -> { + if (n.getCatchClauses().isEmpty() && !n.getFinallyBlock().isPresent()) { + reporter.report(n, "Try has no finally and no catch."); + } + if (n.getResources().isNonEmpty()) { + reporter.report(n, "Catch with resource is not supported."); + } + }); + protected final Validator noAnnotations = new TreeVisitorValidator((node, reporter) -> { + if (node instanceof AnnotationExpr || node instanceof AnnotationDeclaration) { + reporter.report(node, "Annotations are not supported."); + } + }); + protected final Validator noEnums = new SimpleValidator<>(EnumDeclaration.class, + n -> true, + (n, reporter) -> reporter.report(n, "Enumerations are not supported.") + ); + protected final Validator noVarargs = new SimpleValidator<>(Parameter.class, + Parameter::isVarArgs, + (n, reporter) -> reporter.report(n, "Varargs are not supported.") + ); + protected final Validator noForEach = new SimpleValidator<>(ForeachStmt.class, + n -> true, + (n, reporter) -> reporter.report(n, "For-each loops are not supported.") + ); + protected final Validator noStaticImports = new SimpleValidator<>(ImportDeclaration.class, + ImportDeclaration::isStatic, + (n, reporter) -> reporter.report(n, "Static imports are not supported.") + ); + protected final Validator noStringsInSwitch = new SimpleValidator<>(SwitchEntryStmt.class, + n -> n.getLabel().map(l -> l instanceof StringLiteralExpr).orElse(false), + (n, reporter) -> reporter.report(n.getLabel().get(), "Strings in switch statements are not supported.") + ); + protected final Validator noBinaryIntegerLiterals = new NoBinaryIntegerLiteralsValidator(); + protected final Validator noUnderscoresInIntegerLiterals = new NoUnderscoresInIntegerLiteralsValidator(); + protected final Validator noMultiCatch = new SimpleValidator<>(UnionType.class, + n -> true, + (n, reporter) -> reporter.report(n, "Multi-catch is not supported.") + ); + protected final Validator noLambdas = new SimpleValidator<>(LambdaExpr.class, + n -> true, + (n, reporter) -> reporter.report(n, "Lambdas are not supported.") + ); + protected final Validator noModules = new SimpleValidator<>(ModuleDeclaration.class, + n -> true, + (n, reporter) -> reporter.report(n, "Modules are not supported.") + ); + + public Java1_0Validator() { + super(new CommonValidators()); + add(modifiersWithoutStrictfpAndDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods); + add(noAssertKeyword); + add(noInnerClasses); + add(noReflection); + add(noGenerics); + add(tryWithoutResources); + add(noAnnotations); + add(noEnums); + add(noVarargs); + add(noForEach); + add(noStaticImports); + add(noStringsInSwitch); + add(noBinaryIntegerLiterals); + add(noUnderscoresInIntegerLiterals); + add(noMultiCatch); + add(noLambdas); + add(noModules); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_1Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_1Validator.java new file mode 100644 index 000000000..5279b7f95 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_1Validator.java @@ -0,0 +1,22 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.stmt.LocalClassDeclarationStmt; + +/** + * This validator validates according to Java 1.1 syntax rules. + */ +public class Java1_1Validator extends Java1_0Validator { + protected final Validator innerClasses = new SingleNodeTypeValidator<>(ClassOrInterfaceDeclaration.class, + (n, reporter) -> n.getParentNode().ifPresent(p -> { + if (p instanceof LocalClassDeclarationStmt && n.isInterface()) + reporter.report(n, "There is no such thing as a local interface."); + }) + ); + + public Java1_1Validator() { + super(); + replace(noInnerClasses, innerClasses); + remove(noReflection); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_2Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_2Validator.java new file mode 100644 index 000000000..4ac7565b2 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_2Validator.java @@ -0,0 +1,17 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.validator.chunks.ModifierValidator; + +/** + * This validator validates according to Java 1.2 syntax rules. + */ +public class Java1_2Validator extends Java1_1Validator { + protected final Validator modifiersWithoutDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods = new ModifierValidator(true, false, false); + protected final Validator strictfpNotAllowed = new ReservedKeywordValidator("strictfp"); + + public Java1_2Validator() { + super(); + replace(modifiersWithoutStrictfpAndDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods, modifiersWithoutDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods); + add(strictfpNotAllowed); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_3Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_3Validator.java new file mode 100644 index 000000000..72830a117 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_3Validator.java @@ -0,0 +1,10 @@ +package com.github.javaparser.ast.validator; + +/** + * This validator validates according to Java 1.3 syntax rules. + */ +public class Java1_3Validator extends Java1_2Validator { + public Java1_3Validator() { + super(); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_4Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_4Validator.java new file mode 100644 index 000000000..2d4ad5e86 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java1_4Validator.java @@ -0,0 +1,11 @@ +package com.github.javaparser.ast.validator; + +/** + * This validator validates according to Java 1.4 syntax rules. + */ +public class Java1_4Validator extends Java1_3Validator { + public Java1_4Validator() { + super(); + remove(noAssertKeyword); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java5Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java5Validator.java new file mode 100644 index 000000000..d8b1aab1b --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java5Validator.java @@ -0,0 +1,52 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments; +import com.github.javaparser.ast.type.PrimitiveType; +import com.github.javaparser.ast.type.Type; + +import java.util.Optional; + +/** + * This validator validates according to Java 5 syntax rules. + */ +public class Java5Validator extends Java1_4Validator { + Validator genericsWithoutDiamondOperator = new TreeVisitorValidator((node, reporter) -> { + if (node instanceof NodeWithTypeArguments) { + Optional<NodeList<Type>> typeArguments = ((NodeWithTypeArguments<? extends Node>) node).getTypeArguments(); + if (typeArguments.isPresent() && typeArguments.get().isEmpty()) { + reporter.report(node, "The diamond operator is not supported."); + } + } + }); + + protected Validator noPrimitiveGenericArguments = new TreeVisitorValidator((node, reporter) -> { + if (node instanceof NodeWithTypeArguments) { + Optional<NodeList<Type>> typeArguments = ((NodeWithTypeArguments<? extends Node>) node).getTypeArguments(); + typeArguments.ifPresent(types -> types.forEach(ty -> { + if (ty instanceof PrimitiveType) { + reporter.report(node, "Type arguments may not be primitive."); + } + })); + } + }); + + protected final Validator enumNotAllowed = new ReservedKeywordValidator("enum"); + + public Java5Validator() { + super(); + replace(noGenerics, genericsWithoutDiamondOperator); + add(noPrimitiveGenericArguments); + add(enumNotAllowed); + + // TODO validate annotations on classes, fields and methods but nowhere else + // The following is probably too simple. + remove(noAnnotations); + + remove(noEnums); + remove(noVarargs); + remove(noForEach); + remove(noStaticImports); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java6Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java6Validator.java new file mode 100644 index 000000000..705400318 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java6Validator.java @@ -0,0 +1,10 @@ +package com.github.javaparser.ast.validator; + +/** + * This validator validates according to Java 6 syntax rules. + */ +public class Java6Validator extends Java5Validator{ + public Java6Validator() { + super(); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java7Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java7Validator.java new file mode 100644 index 000000000..995e83620 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java7Validator.java @@ -0,0 +1,39 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.stmt.TryStmt; +import com.github.javaparser.ast.type.UnionType; + +/** + * This validator validates according to Java 7 syntax rules. + */ +public class Java7Validator extends Java6Validator { + protected final SingleNodeTypeValidator<TryStmt> tryWithLimitedResources = new SingleNodeTypeValidator<>(TryStmt.class, (n, reporter) -> { + if (n.getCatchClauses().isEmpty() + && n.getResources().isEmpty() + && !n.getFinallyBlock().isPresent()) { + reporter.report(n, "Try has no finally, no catch, and no resources."); + } + for (Expression resource : n.getResources()) { + if (!resource.isVariableDeclarationExpr()) { + reporter.report(n, "Try with resources only supports variable declarations."); + } + } + }); + protected final SingleNodeTypeValidator<UnionType> multiCatch = new SingleNodeTypeValidator<>(UnionType.class, (n, reporter) -> { + // Case "0 elements" is caught elsewhere. + if (n.getElements().size() == 1) { + reporter.report(n, "Union type (multi catch) must have at least two elements."); + } + }); + + public Java7Validator() { + super(); + remove(genericsWithoutDiamondOperator); + replace(tryWithoutResources, tryWithLimitedResources); + remove(noStringsInSwitch); + remove(noBinaryIntegerLiterals); + remove(noUnderscoresInIntegerLiterals); + replace(noMultiCatch, multiCatch); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java8Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java8Validator.java new file mode 100644 index 000000000..e6aca92d0 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java8Validator.java @@ -0,0 +1,32 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.validator.chunks.ModifierValidator; + +/** + * This validator validates according to Java 7 syntax rules. + */ +public class Java8Validator extends Java7Validator { + protected final Validator modifiersWithoutPrivateInterfaceMethods = new ModifierValidator(true, true, false); + protected final Validator defaultMethodsInInterface = new SingleNodeTypeValidator<>(ClassOrInterfaceDeclaration.class, + (n, reporter) -> { + if (n.isInterface()) { + n.getMethods().forEach(m -> { + if (m.isDefault() && !m.getBody().isPresent()) { + reporter.report(m, "'default' methods must have a body."); + } + }); + } + } + ); + + public Java8Validator() { + super(); + replace(modifiersWithoutDefaultAndStaticInterfaceMethodsAndPrivateInterfaceMethods, modifiersWithoutPrivateInterfaceMethods); + add(defaultMethodsInInterface); + remove(noLambdas); + + // TODO validate more annotation locations http://openjdk.java.net/jeps/104 + // TODO validate repeating annotations http://openjdk.java.net/jeps/120 + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java9Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java9Validator.java new file mode 100644 index 000000000..2175c9af3 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Java9Validator.java @@ -0,0 +1,30 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.stmt.TryStmt; +import com.github.javaparser.ast.validator.chunks.ModifierValidator; +import com.github.javaparser.ast.validator.chunks.UnderscoreKeywordValidator; + +/** + * This validator validates according to Java 9 syntax rules. + */ +public class Java9Validator extends Java8Validator { + protected final Validator underscoreKeywordValidator = new UnderscoreKeywordValidator(); + protected final Validator modifiers = new ModifierValidator(true, true, true); + protected final SingleNodeTypeValidator<TryStmt> tryWithResources = new SingleNodeTypeValidator<>(TryStmt.class, (n, reporter) -> { + if (n.getCatchClauses().isEmpty() + && n.getResources().isEmpty() + && !n.getFinallyBlock().isPresent()) { + reporter.report(n, "Try has no finally, no catch, and no resources."); + } + }); + + public Java9Validator() { + super(); + add(underscoreKeywordValidator); + remove(noModules); + replace(modifiersWithoutPrivateInterfaceMethods, modifiers); + replace(tryWithLimitedResources, tryWithResources); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/NoProblemsValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/NoProblemsValidator.java new file mode 100644 index 000000000..002668892 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/NoProblemsValidator.java @@ -0,0 +1,16 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ParserConfiguration; +import com.github.javaparser.ast.Node; + +/** + * Stub validator for when no validation is wanted. + * + * @deprecated when setting a language validator, try {@link com.github.javaparser.ParserConfiguration#setLanguageLevel(ParserConfiguration.LanguageLevel)} with RAW. + */ +@Deprecated +public final class NoProblemsValidator implements Validator { + @Override + public void accept(Node node, ProblemReporter problemReporter) { + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ProblemReporter.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ProblemReporter.java new file mode 100644 index 000000000..eb54810c1 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ProblemReporter.java @@ -0,0 +1,34 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.Problem; +import com.github.javaparser.TokenRange; +import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange; + +import java.util.function.Consumer; + +import static com.github.javaparser.utils.CodeGenerationUtils.f; + +/** + * A simple interface where validators can report found problems. + */ +public class ProblemReporter { + private final Consumer<Problem> problemConsumer; + + public ProblemReporter(Consumer<Problem> problemConsumer) { + this.problemConsumer = problemConsumer; + } + + /** + * Report a problem. + * + * @param message description of the problem + * @param node the node in which the problem occurred, used to find the Range of the problem. + */ + public void report(NodeWithTokenRange<?> node, String message, Object... args) { + report(node.getTokenRange().orElse(null), message, args); + } + + public void report(TokenRange range, String message, Object... args) { + problemConsumer.accept(new Problem(f(message, args), range, null)); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ReservedKeywordValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ReservedKeywordValidator.java new file mode 100644 index 000000000..51a08bdca --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/ReservedKeywordValidator.java @@ -0,0 +1,36 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.expr.Name; +import com.github.javaparser.ast.expr.SimpleName; + +import static com.github.javaparser.utils.CodeGenerationUtils.f; + +/** + * Validates that identifiers are not keywords - this for the few keywords that the parser + * accepts because they were added after Java 1.0. + */ +public class ReservedKeywordValidator extends VisitorValidator { + private final String keyword; + private final String error; + + public ReservedKeywordValidator(String keyword) { + this.keyword = keyword; + error = f("'%s' cannot be used as an identifier as it is a keyword.", keyword); + } + + @Override + public void visit(Name n, ProblemReporter arg) { + if (n.getIdentifier().equals(keyword)) { + arg.report(n, error); + } + super.visit(n, arg); + } + + @Override + public void visit(SimpleName n, ProblemReporter arg) { + if (n.getIdentifier().equals(keyword)) { + arg.report(n, error); + } + super.visit(n, arg); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SimpleValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SimpleValidator.java new file mode 100644 index 000000000..b705b9146 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SimpleValidator.java @@ -0,0 +1,20 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; + +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + * Runs a validator on all nodes of a certain type, + * and adds a problem for all nodes that pass a condition. + */ +public class SimpleValidator<N extends Node> extends SingleNodeTypeValidator<N> { + public SimpleValidator(Class<N> type, Predicate<N> condition, BiConsumer<N, ProblemReporter> problemSupplier) { + super(type, (node, problemReporter) -> { + if (condition.test(node)) { + problemSupplier.accept(node, problemReporter); + } + }); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SingleNodeTypeValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SingleNodeTypeValidator.java new file mode 100644 index 000000000..169849489 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/SingleNodeTypeValidator.java @@ -0,0 +1,24 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; + +/** + * Runs a validator on all nodes of a certain type. + */ +public class SingleNodeTypeValidator<N extends Node> implements Validator { + private final Class<N> type; + private final TypedValidator<N> validator; + + public SingleNodeTypeValidator(Class<N> type, TypedValidator<N> validator) { + this.type = type; + this.validator = validator; + } + + @Override + public void accept(Node node, ProblemReporter problemReporter) { + if (type.isInstance(node)) { + validator.accept(type.cast(node), problemReporter); + } + node.findAll(type).forEach(n -> validator.accept(n, problemReporter)); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TreeVisitorValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TreeVisitorValidator.java new file mode 100644 index 000000000..ae8e049e0 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TreeVisitorValidator.java @@ -0,0 +1,22 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; + +/** + * A validator that walks the whole tree, visiting every node. + */ +public class TreeVisitorValidator implements Validator { + private final Validator validator; + + public TreeVisitorValidator(Validator validator) { + this.validator = validator; + } + + @Override + public final void accept(Node node, ProblemReporter reporter) { + validator.accept(node, reporter); + for (Node child : node.getChildNodes()) { + accept(child, reporter); + } + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TypedValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TypedValidator.java new file mode 100644 index 000000000..588c53f11 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/TypedValidator.java @@ -0,0 +1,24 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ParseResult; +import com.github.javaparser.ast.Node; + +import java.util.function.BiConsumer; + +/** + * A validator that validates a known node type. + */ +public interface TypedValidator<N extends Node> extends BiConsumer<N, ProblemReporter> { + /** + * @param node the node that wants to be validated + * @param problemReporter when found, validation errors can be reported here + */ + void accept(N node, ProblemReporter problemReporter); + + @SuppressWarnings("unchecked") + default ParseResult.PostProcessor postProcessor() { + return (result, configuration) -> + result.getResult().ifPresent(node -> + accept((N) node, new ProblemReporter(problem -> result.getProblems().add(problem)))); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validator.java new file mode 100644 index 000000000..0d5752a03 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validator.java @@ -0,0 +1,15 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; + +/** + * A validator that can be run on a node to check for semantic errors. + * It is fully up to the implementor how to do this. + */ +public interface Validator extends TypedValidator<Node> { + /** + * @param node the node that wants to be validated + * @param problemReporter when found, validation errors can be reported here + */ + void accept(Node node, ProblemReporter problemReporter); +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validators.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validators.java new file mode 100644 index 000000000..dd6926ab7 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/Validators.java @@ -0,0 +1,45 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A validator that will call a collection of validators. + */ +public class Validators implements Validator { + private final List<Validator> validators = new ArrayList<>(); + + public Validators(Validator... validators) { + this.validators.addAll(Arrays.asList(validators)); + } + + public List<Validator> getValidators() { + return validators; + } + + public Validators remove(Validator validator) { + if (!validators.remove(validator)) { + throw new AssertionError("Trying to remove a validator that isn't there."); + } + return this; + } + + public Validators replace(Validator oldValidator, Validator newValidator) { + remove(oldValidator); + add(newValidator); + return this; + } + + public Validators add(Validator newValidator) { + validators.add(newValidator); + return this; + } + + @Override + public void accept(Node node, ProblemReporter problemReporter) { + validators.forEach(v -> v.accept(node, problemReporter)); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/VisitorValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/VisitorValidator.java new file mode 100644 index 000000000..547092d67 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/VisitorValidator.java @@ -0,0 +1,16 @@ +package com.github.javaparser.ast.validator; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +/** + * A validator that uses a visitor for validation. + * This class is the visitor too. + * Implement the "visit" methods you want to use for validation. + */ +public abstract class VisitorValidator extends VoidVisitorAdapter<ProblemReporter> implements Validator { + @Override + public void accept(Node node, ProblemReporter problemReporter) { + node.accept(this, problemReporter); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/CommonValidators.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/CommonValidators.java new file mode 100644 index 000000000..5e5b7919e --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/CommonValidators.java @@ -0,0 +1,70 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.InitializerDeclaration; +import com.github.javaparser.ast.expr.*; +import com.github.javaparser.ast.validator.SimpleValidator; +import com.github.javaparser.ast.validator.SingleNodeTypeValidator; +import com.github.javaparser.ast.validator.TreeVisitorValidator; +import com.github.javaparser.ast.validator.Validators; +import com.github.javaparser.metamodel.NodeMetaModel; +import com.github.javaparser.metamodel.PropertyMetaModel; + +import java.util.Optional; + +/** + * Contains validations that are valid for every Java version. + */ +public class CommonValidators extends Validators { + public CommonValidators() { + super( + new SimpleValidator<>(ClassOrInterfaceDeclaration.class, + n -> !n.isInterface() && n.getExtendedTypes().size() > 1, + (n, reporter) -> reporter.report(n.getExtendedTypes(1), "A class cannot extend more than one other class.") + ), + new SimpleValidator<>(ClassOrInterfaceDeclaration.class, + n -> n.isInterface() && !n.getImplementedTypes().isEmpty(), + (n, reporter) -> reporter.report(n.getImplementedTypes(0), "An interface cannot implement other interfaces.") + ), + new SingleNodeTypeValidator<>(ClassOrInterfaceDeclaration.class, (n, reporter) -> { + if (n.isInterface()) { + n.getMembers().forEach(mem -> { + if (mem instanceof InitializerDeclaration) { + reporter.report(mem, "An interface cannot have initializers."); + } + }); + } + } + ), + new SingleNodeTypeValidator<>(AssignExpr.class, (n, reporter) -> { + // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26 + Expression target = n.getTarget(); + while (target instanceof EnclosedExpr) { + target = ((EnclosedExpr) target).getInner(); + } + if (target instanceof NameExpr + || target instanceof ArrayAccessExpr + || target instanceof FieldAccessExpr) { + return; + } + reporter.report(n.getTarget(), "Illegal left hand side of an assignment."); + } + ), + new TreeVisitorValidator((node, problemReporter) -> { + NodeMetaModel mm = node.getMetaModel(); + for (PropertyMetaModel ppm : mm.getAllPropertyMetaModels()) { + if (ppm.isNonEmpty()) { + if (ppm.isNodeList()) { + NodeList value = (NodeList) ppm.getValue(node); + if (value.isEmpty()) { + problemReporter.report(node, "%s.%s can not be empty.", mm.getTypeName(), ppm.getName()); + } + } + // No need to check empty strings, it should be impossible to set them to "" + } + } + }) + ); + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java new file mode 100644 index 000000000..0a01c3349 --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/ModifierValidator.java @@ -0,0 +1,211 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.body.*; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.modules.ModuleRequiresStmt; +import com.github.javaparser.ast.nodeTypes.NodeWithModifiers; +import com.github.javaparser.ast.nodeTypes.NodeWithTokenRange; +import com.github.javaparser.ast.stmt.CatchClause; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.VisitorValidator; +import com.github.javaparser.utils.SeparatedItemStringBuilder; + +import java.util.ArrayList; +import java.util.List; + +import static com.github.javaparser.ast.Modifier.*; +import static java.util.Arrays.asList; + + +/** + * Verifies that only allowed modifiers are used where modifiers are expected. + */ +public class ModifierValidator extends VisitorValidator { + private final Modifier[] interfaceWithNothingSpecial = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, FINAL, SYNCHRONIZED, NATIVE, STRICTFP}; + private final Modifier[] interfaceWithStaticAndDefault = new Modifier[]{PUBLIC, PROTECTED, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT}; + private final Modifier[] interfaceWithStaticAndDefaultAndPrivate = new Modifier[]{PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP, DEFAULT}; + + private final boolean hasStrictfp; + private final boolean hasDefaultAndStaticInterfaceMethods; + private final boolean hasPrivateInterfaceMethods; + + public ModifierValidator(boolean hasStrictfp, boolean hasDefaultAndStaticInterfaceMethods, boolean hasPrivateInterfaceMethods) { + this.hasStrictfp = hasStrictfp; + this.hasDefaultAndStaticInterfaceMethods = hasDefaultAndStaticInterfaceMethods; + this.hasPrivateInterfaceMethods = hasPrivateInterfaceMethods; + } + + @Override + public void visit(ClassOrInterfaceDeclaration n, ProblemReporter reporter) { + if (n.isInterface()) { + validateInterfaceModifiers(n, reporter); + } else { + validateClassModifiers(n, reporter); + } + super.visit(n, reporter); + } + + private void validateClassModifiers(ClassOrInterfaceDeclaration n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT, FINAL, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP); + } else if (n.isLocalClassDeclaration()) { + validateModifiers(n, reporter, ABSTRACT, FINAL, STRICTFP); + } + } + + private void validateInterfaceModifiers(TypeDeclaration<?> n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, STRICTFP); + } + } + + @Override + public void visit(EnumDeclaration n, ProblemReporter reporter) { + if (n.isTopLevelType()) { + validateModifiers(n, reporter, PUBLIC, STRICTFP); + } else if (n.isNestedType()) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, STRICTFP); + } + super.visit(n, reporter); + } + + @Override + public void visit(AnnotationDeclaration n, ProblemReporter reporter) { + validateInterfaceModifiers(n, reporter); + super.visit(n, reporter); + } + + @Override + public void visit(AnnotationMemberDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, ABSTRACT); + super.visit(n, reporter); + } + + @Override + public void visit(ConstructorDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE); + n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL)); + super.visit(n, reporter); + } + + @Override + public void visit(FieldDeclaration n, ProblemReporter reporter) { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, STATIC, FINAL, TRANSIENT, VOLATILE); + super.visit(n, reporter); + } + + @Override + public void visit(MethodDeclaration n, ProblemReporter reporter) { + if (n.isAbstract()) { + final SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Cannot be 'abstract' and also '", "', '", "'."); + for (Modifier m : asList(PRIVATE, STATIC, FINAL, NATIVE, STRICTFP, SYNCHRONIZED)) { + if (n.getModifiers().contains(m)) { + builder.append(m.asString()); + } + } + if (builder.hasItems()) { + reporter.report(n, builder.toString()); + } + } + if (n.getParentNode().isPresent()) { + if (n.getParentNode().get() instanceof ClassOrInterfaceDeclaration) { + if (((ClassOrInterfaceDeclaration) n.getParentNode().get()).isInterface()) { + if (hasDefaultAndStaticInterfaceMethods) { + if (hasPrivateInterfaceMethods) { + validateModifiers(n, reporter, interfaceWithStaticAndDefaultAndPrivate); + } else { + validateModifiers(n, reporter, interfaceWithStaticAndDefault); + } + } else { + validateModifiers(n, reporter, interfaceWithNothingSpecial); + } + } else { + validateModifiers(n, reporter, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, SYNCHRONIZED, NATIVE, STRICTFP); + } + } + } + n.getParameters().forEach(p -> validateModifiers(p, reporter, FINAL)); + super.visit(n, reporter); + } + + @Override + public void visit(LambdaExpr n, ProblemReporter reporter) { + n.getParameters().forEach(p -> { + // Final is not allowed on inferred parameters, but those get caught by the parser. + validateModifiers(p, reporter, FINAL); + }); + super.visit(n, reporter); + } + + @Override + public void visit(CatchClause n, ProblemReporter reporter) { + validateModifiers(n.getParameter(), reporter, FINAL); + super.visit(n, reporter); + } + + @Override + public void visit(VariableDeclarationExpr n, ProblemReporter reporter) { + validateModifiers(n, reporter, FINAL); + super.visit(n, reporter); + } + + @Override + public void visit(ModuleRequiresStmt n, ProblemReporter reporter) { + validateModifiers(n, reporter, TRANSITIVE, STATIC); + super.visit(n, reporter); + } + + private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateModifiers(T n, ProblemReporter reporter, Modifier... allowedModifiers) { + validateAtMostOneOf(n, reporter, PUBLIC, PROTECTED, PRIVATE); + validateAtMostOneOf(n, reporter, FINAL, ABSTRACT); + if (hasStrictfp) { + validateAtMostOneOf(n, reporter, NATIVE, STRICTFP); + } else { + allowedModifiers = removeModifierFromArray(STRICTFP, allowedModifiers); + } + for (Modifier m : n.getModifiers()) { + if (!arrayContains(allowedModifiers, m)) { + reporter.report(n, "'%s' is not allowed here.", m.asString()); + } + } + } + + private Modifier[] removeModifierFromArray(Modifier m, Modifier[] allowedModifiers) { + final List<Modifier> newModifiers = new ArrayList<>(asList(allowedModifiers)); + newModifiers.remove(m); + allowedModifiers = newModifiers.toArray(new Modifier[0]); + return allowedModifiers; + } + + private boolean arrayContains(Object[] items, Object searchItem) { + for (Object o : items) { + if (o == searchItem) { + return true; + } + } + return false; + } + + private <T extends NodeWithModifiers<?> & NodeWithTokenRange<?>> void validateAtMostOneOf(T t, ProblemReporter reporter, Modifier... modifiers) { + List<Modifier> foundModifiers = new ArrayList<>(); + for (Modifier m : modifiers) { + if (t.getModifiers().contains(m)) { + foundModifiers.add(m); + } + } + if (foundModifiers.size() > 1) { + SeparatedItemStringBuilder builder = new SeparatedItemStringBuilder("Can have only one of '", "', '", "'."); + for (Modifier m : foundModifiers) { + builder.append(m.asString()); + } + reporter.report(t, builder.toString()); + } + } + +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoBinaryIntegerLiteralsValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoBinaryIntegerLiteralsValidator.java new file mode 100644 index 000000000..82e8dd7cc --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoBinaryIntegerLiteralsValidator.java @@ -0,0 +1,27 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.expr.IntegerLiteralExpr; +import com.github.javaparser.ast.expr.LiteralStringValueExpr; +import com.github.javaparser.ast.expr.LongLiteralExpr; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.VisitorValidator; + +public class NoBinaryIntegerLiteralsValidator extends VisitorValidator { + @Override + public void visit(IntegerLiteralExpr n, ProblemReporter arg) { + validate(n, arg); + super.visit(n, arg); + } + + @Override + public void visit(LongLiteralExpr n, ProblemReporter arg) { + validate(n, arg); + super.visit(n, arg); + } + + private static void validate(LiteralStringValueExpr n, ProblemReporter arg) { + if (n.getValue().toUpperCase().startsWith("0B")) { + arg.report(n, "Binary literal values are not supported."); + } + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoUnderscoresInIntegerLiteralsValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoUnderscoresInIntegerLiteralsValidator.java new file mode 100644 index 000000000..901b7348f --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/NoUnderscoresInIntegerLiteralsValidator.java @@ -0,0 +1,27 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.expr.IntegerLiteralExpr; +import com.github.javaparser.ast.expr.LiteralStringValueExpr; +import com.github.javaparser.ast.expr.LongLiteralExpr; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.VisitorValidator; + +public class NoUnderscoresInIntegerLiteralsValidator extends VisitorValidator { + @Override + public void visit(IntegerLiteralExpr n, ProblemReporter arg) { + validate(n, arg); + super.visit(n, arg); + } + + @Override + public void visit(LongLiteralExpr n, ProblemReporter arg) { + validate(n, arg); + super.visit(n, arg); + } + + private static void validate(LiteralStringValueExpr n, ProblemReporter arg) { + if (n.getValue().contains("_")) { + arg.report(n, "Underscores in literal values are not supported."); + } + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/UnderscoreKeywordValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/UnderscoreKeywordValidator.java new file mode 100644 index 000000000..e812597bb --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/UnderscoreKeywordValidator.java @@ -0,0 +1,27 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.expr.Name; +import com.github.javaparser.ast.expr.SimpleName; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.VisitorValidator; + +public class UnderscoreKeywordValidator extends VisitorValidator { + @Override + public void visit(Name n, ProblemReporter arg) { + validateIdentifier(n, n.getIdentifier(), arg); + super.visit(n, arg); + } + + @Override + public void visit(SimpleName n, ProblemReporter arg) { + validateIdentifier(n, n.getIdentifier(), arg); + super.visit(n, arg); + } + + private static void validateIdentifier(Node n, String id, ProblemReporter arg) { + if (id.equals("_")) { + arg.report(n, "'_' is a reserved keyword."); + } + } +} diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/VarValidator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/VarValidator.java new file mode 100644 index 000000000..19357e83c --- /dev/null +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/validator/chunks/VarValidator.java @@ -0,0 +1,93 @@ +package com.github.javaparser.ast.validator.chunks; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.expr.ArrayCreationExpr; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.NullLiteralExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.stmt.ForStmt; +import com.github.javaparser.ast.stmt.ForeachStmt; +import com.github.javaparser.ast.type.VarType; +import com.github.javaparser.ast.validator.ProblemReporter; +import com.github.javaparser.ast.validator.TypedValidator; + +import java.util.Optional; + +public class VarValidator implements TypedValidator<VarType> { + private boolean varAllowedInLambdaParameters; + + public VarValidator(boolean varAllowedInLambdaParameters) { + this.varAllowedInLambdaParameters = varAllowedInLambdaParameters; + } + + @Override + public void accept(VarType node, ProblemReporter reporter) { + // All allowed locations are within a VariableDeclaration inside a VariableDeclarationExpr inside something else. + Optional<VariableDeclarator> variableDeclarator = node.findParent(VariableDeclarator.class); + if (!variableDeclarator.isPresent()) { + // Java 11's var in lambda's + if (varAllowedInLambdaParameters) { + boolean valid = node + .findParent(Parameter.class) + .flatMap(Node::getParentNode) + .map((Node p) -> p instanceof LambdaExpr).orElse(false); + if (valid) { + return; + } + } + reportIllegalPosition(node, reporter); + return; + } + variableDeclarator.ifPresent(vd -> { + Optional<Node> variableDeclarationExpr = vd.getParentNode(); + if (!variableDeclarationExpr.isPresent()) { + reportIllegalPosition(node, reporter); + return; + } + variableDeclarationExpr.ifPresent(vdeNode -> { + if (!(vdeNode instanceof VariableDeclarationExpr)) { + reportIllegalPosition(node, reporter); + return; + } + VariableDeclarationExpr vde = (VariableDeclarationExpr) vdeNode; + if (vde.getVariables().size() > 1) { + reporter.report(vde, "\"var\" only takes a single variable."); + } + Optional<Node> container = vdeNode.getParentNode(); + if (!container.isPresent()) { + reportIllegalPosition(node, reporter); + return; + } + container.ifPresent(c -> { + boolean positionIsFine = c instanceof ForStmt || c instanceof ForeachStmt || c instanceof ExpressionStmt; + if (!positionIsFine) { + reportIllegalPosition(node, reporter); + } + // A local variable declaration ends up inside an ExpressionStmt. + if (c instanceof ExpressionStmt) { + if (!vd.getInitializer().isPresent()) { + reporter.report(node, "\"var\" needs an initializer."); + } + vd.getInitializer().ifPresent(initializer -> { + if (initializer instanceof NullLiteralExpr) { + reporter.report(node, "\"var\" cannot infer type from just null."); + } + if (initializer instanceof ArrayCreationExpr) { + reporter.report(node, "\"var\" cannot infer array types."); + } + }); + + } + }); + }); + }); + + } + + private void reportIllegalPosition(VarType n, ProblemReporter reporter) { + reporter.report(n, "\"var\" is not allowed here."); + } +} |