From d4b1f8c1d78dc1b6f9c3b0fbbd9e7414fcb01f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 2 Nov 2026 11:56:01 +0100 Subject: [PATCH 1/5] Separate compiler diagnostics and annotation extraction from shared policy --- .../java/model/EcjDiagnosticProvider.java | 67 ++++++++++ .../sonar/java/model/EcjSymbolMetadata.java | 112 ++++++++++++++++ .../java/org/sonar/java/model/JParser.java | 2 +- .../org/sonar/java/model/JParserConfig.java | 2 +- .../java/org/sonar/java/model/JProblem.java | 46 ++----- .../main/java/org/sonar/java/model/JSema.java | 8 +- .../java/org/sonar/java/model/JSymbol.java | 8 +- .../org/sonar/java/model/JSymbolMetadata.java | 126 +++--------------- .../org/sonar/java/model/JVariableSymbol.java | 2 +- .../java/org/sonar/java/model/JWarning.java | 29 +--- .../sonar/java/model/ClassesLayoutTest.java | 2 +- .../sonar/java/model/JSymbolMetadataTest.java | 19 ++- .../org/sonar/java/model/SymbolsTest.java | 3 +- 13 files changed, 242 insertions(+), 184 deletions(-) create mode 100644 java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java create mode 100644 java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java b/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java new file mode 100644 index 00000000000..4539f4241c6 --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java @@ -0,0 +1,67 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.core.dom.CompilationUnit; + +final class EcjDiagnosticProvider { + + private static final Map IDS = Map.of( + JProblem.Type.UNDEFINED_TYPE, IProblem.UndefinedType, + JProblem.Type.PREVIEW_FEATURE_USED, IProblem.PreviewFeatureUsed, + JProblem.Type.UNUSED_IMPORT, IProblem.UnusedImport, + JProblem.Type.REDUNDANT_CAST, IProblem.UnnecessaryCast, + JProblem.Type.ASSIGNMENT_HAS_NO_EFFECT, IProblem.AssignmentHasNoEffect, + JProblem.Type.MASKED_CATCH, IProblem.MaskedCatch); + + private EcjDiagnosticProvider() { + } + + static Set compilerOptions() { + return Set.of( + JavaCore.COMPILER_PB_UNUSED_IMPORT, + JavaCore.COMPILER_PB_UNNECESSARY_TYPE_CHECK, + JavaCore.COMPILER_PB_NO_EFFECT_ASSIGNMENT, + JavaCore.COMPILER_PB_HIDDEN_CATCH_BLOCK + ); + } + + static List warnings(CompilationUnit ast, LineColumnConverter positions) { + return Stream.of(ast.getProblems()) + .map(problem -> convert(problem, positions)) + .filter(Objects::nonNull) + .toList(); + } + + private static JWarning convert(IProblem problem, LineColumnConverter positions) { + for (JProblem.Type type : JProblem.Type.values()) { + if (problem.getID() == IDS.get(type)) { + LineColumnConverter.Pos start = positions.toPos(problem.getSourceStart()); + LineColumnConverter.Pos end = positions.toPos(problem.getSourceStart()); + return new JWarning(problem.getMessage(), type, start.line(), start.columnOffset(), end.line(), end.columnOffset()); + } + } + return null; + } +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java b/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java new file mode 100644 index 00000000000..8e97a4f7005 --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java @@ -0,0 +1,112 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.eclipse.jdt.core.dom.IAnnotationBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.SymbolMetadata.AnnotationInstance; +import org.sonar.plugins.java.api.semantic.SymbolMetadata.AnnotationValue; + +final class EcjSymbolMetadata { + + private EcjSymbolMetadata() { + } + + static JSymbolMetadata declaration(JSema sema, Symbol owner, IAnnotationBinding[] bindings) { + return metadata(sema, owner, bindings, new JSymbolMetadata[0], false); + } + + static JSymbolMetadata of(JSema sema, Symbol owner, ITypeBinding type, IAnnotationBinding[] externalAnnotations) { + var symbolAnnotations = new IAnnotationBinding[externalAnnotations.length + type.getTypeAnnotations().length]; + System.arraycopy(externalAnnotations, 0, symbolAnnotations, 0, externalAnnotations.length); + System.arraycopy(type.getTypeAnnotations(), 0, symbolAnnotations, externalAnnotations.length, type.getTypeAnnotations().length); + + var parameterMetaDatas = Arrays.stream(type.getTypeArguments()) + .map(param -> ofTypeArg(sema, owner, param)) + .toArray(JSymbolMetadata[]::new); + + return metadata(sema, owner, symbolAnnotations, parameterMetaDatas, false); + } + + private static JSymbolMetadata ofTypeArg(JSema sema, Symbol owner, ITypeBinding param) { + var symbolAnnotations = param.getTypeAnnotations(); + var parameterMetaDatas = Arrays.stream(param.getTypeArguments()) + .map(p -> ofTypeArg(sema, owner, p)) + .toArray(JSymbolMetadata[]::new); + return metadata(sema, owner, symbolAnnotations, parameterMetaDatas, true); + } + + private static JSymbolMetadata metadata(JSema sema, Symbol owner, IAnnotationBinding[] bindings, + JSymbolMetadata[] parameters, boolean typeArgument) { + return new JSymbolMetadata(owner, () -> Arrays.stream(bindings).map(sema::annotation).collect(Collectors.toList()), parameters, typeArgument); + } + + static final class JAnnotationInstance implements AnnotationInstance { + private final JSema sema; + private final IAnnotationBinding annotationBinding; + + /** + * Cache for {@link #values()}. + */ + private List values; + + JAnnotationInstance(JSema sema, IAnnotationBinding annotationBinding) { + this.sema = sema; + this.annotationBinding = annotationBinding; + } + + @Override + public Symbol symbol() { + return sema.typeSymbol(annotationBinding.getAnnotationType()); + } + + @Override + public List values() { + if (values == null) { + values = Arrays.stream(annotationBinding.getDeclaredMemberValuePairs()) + .map(p -> new AnnotationValueImpl(p.getName(), convertAnnotationValue(p.getValue()))) + .collect(Collectors.toList()); + } + return values; + } + + private Object convertAnnotationValue(Object value) { + return EcjSymbolMetadata.convertAnnotationValue(sema, value); + } + } + + static Object convertAnnotationValue(JSema sema, Object value) { + return switch (value) { + case IVariableBinding iVariableBinding -> sema.variableSymbol(iVariableBinding); + case ITypeBinding iTypeBinding -> sema.typeSymbol(iTypeBinding); + case IAnnotationBinding iAnnotationBinding -> sema.annotation(iAnnotationBinding); + case Object[] a -> { + Object[] result = new Object[a.length]; + for (int i = 0; i < a.length; i++) { + result[i] = convertAnnotationValue(sema, a[i]); + } + yield result; + } + case null, default -> value; + }; + } +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/JParser.java b/java-frontend/src/main/java/org/sonar/java/model/JParser.java index e58aeb7f024..cd47482e4cd 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JParser.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JParser.java @@ -319,7 +319,7 @@ static JavaTree.CompilationUnitTreeImpl convert(String version, String unitName, JavaTree.CompilationUnitTreeImpl tree = converter.convertCompilationUnit(astNode); tree.sema = converter.sema; - JWarning.Mapper.warningsFor(astNode, converter.lineColumnConverter).mappedInto(tree); + JWarning.Mapper.warningsFor(EcjDiagnosticProvider.warnings(astNode, converter.lineColumnConverter)).mappedInto(tree); ASTUtils.mayTolerateMissingType(astNode.getAST()); diff --git a/java-frontend/src/main/java/org/sonar/java/model/JParserConfig.java b/java-frontend/src/main/java/org/sonar/java/model/JParserConfig.java index 79cffd79a90..5ef2bb5400c 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JParserConfig.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JParserConfig.java @@ -125,7 +125,7 @@ public ASTParser astParser() { options.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, "enabled"); } // enabling all supported compiler warnings - JProblem.Type.compilerOptions() + EcjDiagnosticProvider.compilerOptions() .forEach(option -> options.put(option, "warning")); astParser.setCompilerOptions(options); diff --git a/java-frontend/src/main/java/org/sonar/java/model/JProblem.java b/java-frontend/src/main/java/org/sonar/java/model/JProblem.java index 38abd459dad..66076614b75 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JProblem.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JProblem.java @@ -16,13 +16,8 @@ */ package org.sonar.java.model; -import java.util.Collections; -import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.stream.Stream; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.compiler.IProblem; import org.sonar.plugins.java.api.tree.Tree; import org.sonarsource.analyzer.commons.collections.SetUtils; @@ -80,44 +75,19 @@ public int hashCode() { } public enum Type { - UNDEFINED_TYPE(IProblem.UndefinedType), - PREVIEW_FEATURE_USED(IProblem.PreviewFeatureUsed), - UNUSED_IMPORT(IProblem.UnusedImport, JavaCore.COMPILER_PB_UNUSED_IMPORT, Tree.Kind.IMPORT), - REDUNDANT_CAST(IProblem.UnnecessaryCast, JavaCore.COMPILER_PB_UNNECESSARY_TYPE_CHECK, Tree.Kind.TYPE_CAST, Tree.Kind.PARENTHESIZED_EXPRESSION), - ASSIGNMENT_HAS_NO_EFFECT(IProblem.AssignmentHasNoEffect, JavaCore.COMPILER_PB_NO_EFFECT_ASSIGNMENT, Tree.Kind.ASSIGNMENT), - MASKED_CATCH(IProblem.MaskedCatch, JavaCore.COMPILER_PB_HIDDEN_CATCH_BLOCK, Tree.Kind.IDENTIFIER, Tree.Kind.MEMBER_SELECT); - - private final int warningID; - private final String compilerOptionKey; - private final Set kinds; - - private static final Set COMPILER_OPTIONS = new HashSet<>(); + UNDEFINED_TYPE(), + PREVIEW_FEATURE_USED(), + UNUSED_IMPORT(Tree.Kind.IMPORT), + REDUNDANT_CAST(Tree.Kind.TYPE_CAST, Tree.Kind.PARENTHESIZED_EXPRESSION), + ASSIGNMENT_HAS_NO_EFFECT(Tree.Kind.ASSIGNMENT), + MASKED_CATCH(Tree.Kind.IDENTIFIER, Tree.Kind.MEMBER_SELECT); - Type(int warningID) { - this.warningID = warningID; - this.compilerOptionKey = null; - this.kinds = Collections.emptySet(); - } + private final Set kinds; - Type(int warningID, String compilerOptionKey, Tree.Kind... kinds) { - this.warningID = warningID; - this.compilerOptionKey = compilerOptionKey; + Type(Tree.Kind... kinds) { this.kinds = SetUtils.immutableSetOf(kinds); } - boolean matches(IProblem warning) { - return warning.getID() == warningID; - } - - public static Set compilerOptions() { - if (COMPILER_OPTIONS.isEmpty()) { - Stream.of(Type.values()) - .map(t -> t.compilerOptionKey) - .forEach(COMPILER_OPTIONS::add); - } - return Collections.unmodifiableSet(COMPILER_OPTIONS); - } - public Set getKinds() { return kinds; } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSema.java b/java-frontend/src/main/java/org/sonar/java/model/JSema.java index 4045f8d1db5..f3c65fd7534 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSema.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSema.java @@ -48,7 +48,7 @@ public final class JSema implements Sema { private final Map symbols = new HashMap<>(); private final Map initializerBlockSymbols = new HashMap<>(); private final Map staticInitializerBlockSymbols = new HashMap<>(); - private final Map annotations = new HashMap<>(); + private final Map annotations = new HashMap<>(); private final Map nameToTypeCache = new HashMap<>(); JSema(AST ast) { @@ -126,12 +126,12 @@ public JVariableSymbol variableSymbol(IVariableBinding variableBinding) { return (JVariableSymbol) symbols.computeIfAbsent(variableBinding, k -> new JVariableSymbol(this, (IVariableBinding) k)); } - JSymbolMetadata.JAnnotationInstance annotation(IAnnotationBinding annotationBinding) { - JSymbolMetadata.JAnnotationInstance cached = annotations.get(annotationBinding); + EcjSymbolMetadata.JAnnotationInstance annotation(IAnnotationBinding annotationBinding) { + EcjSymbolMetadata.JAnnotationInstance cached = annotations.get(annotationBinding); if (cached != null) { return cached; } - return annotations.computeIfAbsent(annotationBinding, k -> new JSymbolMetadata.JAnnotationInstance(this, k)); + return annotations.computeIfAbsent(annotationBinding, k -> new EcjSymbolMetadata.JAnnotationInstance(this, k)); } static IBinding declarationBinding(IBinding binding) { diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java index dcd11673412..54365f512a0 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java @@ -378,11 +378,11 @@ public final SymbolMetadata metadata() { private SymbolMetadata convertMetadata() { switch (binding.getKind()) { case IBinding.PACKAGE: - return new JSymbolMetadata(sema, this, sema.resolvePackageAnnotations(binding.getName())); + return EcjSymbolMetadata.declaration(sema, this, sema.resolvePackageAnnotations(binding.getName())); case IBinding.VARIABLE: ITypeBinding type = ((IVariableBinding) binding).getType(); if (type == null) { - return new JSymbolMetadata(sema, this, binding.getAnnotations()); + return EcjSymbolMetadata.declaration(sema, this, binding.getAnnotations()); } return convertMetadata(type); case IBinding.METHOD: @@ -393,12 +393,12 @@ private SymbolMetadata convertMetadata() { } return convertMetadata(returnType); default: - return new JSymbolMetadata(sema, this, binding.getAnnotations()); + return EcjSymbolMetadata.declaration(sema, this, binding.getAnnotations()); } } private SymbolMetadata convertMetadata(ITypeBinding type) { - return JSymbolMetadata.of(sema, this, type, binding.getAnnotations()); + return EcjSymbolMetadata.of(sema, this, type, binding.getAnnotations()); } /** diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java b/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java index 2b9c5b6b862..422a97356e5 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java @@ -16,6 +16,7 @@ */ package org.sonar.java.model; +import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.List; @@ -23,12 +24,9 @@ import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; +import java.util.function.Supplier; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.IAnnotationBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.tree.AnnotationTree; @@ -45,11 +43,10 @@ final class JSymbolMetadata implements SymbolMetadata { private static final NullabilityData[] UNKNOWN_NULLABILITY = forEachLevel(level -> new JNullabilityData(NullabilityType.UNKNOWN, level, null, null, false)); - private final JSema sema; private final Symbol symbol; - private final IAnnotationBinding[] symbolBindings; + private final Supplier> annotationSource; + private final Supplier> allAnnotationsSource; private final SymbolMetadata[] parameterMetas; - private final IAnnotationBinding[] annotationBindings; /** * Cache for {@link #annotations()}. @@ -65,57 +62,24 @@ final class JSymbolMetadata implements SymbolMetadata { private final boolean isTypeArgMetadata; - JSymbolMetadata(JSema sema, Symbol symbol, IAnnotationBinding[] annotationBindings) { - this.sema = Objects.requireNonNull(sema); - this.symbol = symbol; - this.symbolBindings = annotationBindings; - this.parameterMetas = new SymbolMetadata[0]; - this.isTypeArgMetadata = false; - - this.annotationBindings = annotationBindings; + JSymbolMetadata(Symbol symbol, List annotations) { + this(symbol, () -> annotations, new JSymbolMetadata[0], false); } - private JSymbolMetadata(JSema sema, Symbol symbol, IAnnotationBinding[] symbolAnnotations, JSymbolMetadata[] parameterMetaDatas, boolean isTypeArgMetadata) { - this.sema = Objects.requireNonNull(sema); + JSymbolMetadata(Symbol symbol, Supplier> annotations, + JSymbolMetadata[] parameterMetas, boolean isTypeArgMetadata) { this.symbol = symbol; - this.symbolBindings = symbolAnnotations; - this.parameterMetas = parameterMetaDatas; + this.annotationSource = annotations; + this.parameterMetas = parameterMetas; this.isTypeArgMetadata = isTypeArgMetadata; - - int total = symbolBindings.length; - for (JSymbolMetadata parameterMetaData : parameterMetaDatas) { - total += parameterMetaData.annotationBindings.length; - } - - this.annotationBindings = new IAnnotationBinding[total]; - int current = symbolAnnotations.length; - System.arraycopy(symbolAnnotations, 0, this.annotationBindings, 0, symbolAnnotations.length); - - for (JSymbolMetadata parameterMetaData : parameterMetaDatas) { - var paramBindings = parameterMetaData.annotationBindings; - System.arraycopy(paramBindings, 0, this.annotationBindings, current, paramBindings.length); - current += paramBindings.length; - } - } - - public static JSymbolMetadata of(JSema sema, Symbol owner, ITypeBinding type, IAnnotationBinding[] externalAnnotations) { - var symbolAnnotations = new IAnnotationBinding[externalAnnotations.length + type.getTypeAnnotations().length]; - System.arraycopy(externalAnnotations, 0, symbolAnnotations, 0, externalAnnotations.length); - System.arraycopy(type.getTypeAnnotations(), 0, symbolAnnotations, externalAnnotations.length, type.getTypeAnnotations().length); - - var parameterMetaDatas = Arrays.stream(type.getTypeArguments()) - .map(param -> ofTypeArg(sema, owner, param)) - .toArray(JSymbolMetadata[]::new); - - return new JSymbolMetadata(sema, owner, symbolAnnotations, parameterMetaDatas, false); - } - - private static JSymbolMetadata ofTypeArg(JSema sema, Symbol owner, ITypeBinding param) { - var symbolAnnotations = param.getTypeAnnotations(); - var parameterMetaDatas = Arrays.stream(param.getTypeArguments()) - .map(p -> ofTypeArg(sema, owner, p)) - .toArray(JSymbolMetadata[]::new); - return new JSymbolMetadata(sema, owner, symbolAnnotations, parameterMetaDatas, true); + List>> parameterSources = Arrays.stream(parameterMetas) + .map(metadata -> metadata.allAnnotationsSource) + .toList(); + this.allAnnotationsSource = () -> { + List all = new ArrayList<>(annotationSource.get()); + parameterSources.forEach(source -> all.addAll(source.get())); + return all; + }; } private static NullabilityData[] forEachLevel(Function initializer) { @@ -133,9 +97,7 @@ public static NullabilityData unknownNullabilityAt(NullabilityLevel level) { @Override public List annotations() { if (annotations == null) { - annotations = Arrays.stream(annotationBindings) - .map(sema::annotation) - .collect(Collectors.toList()); + annotations = allAnnotationsSource.get(); } return annotations; } @@ -143,9 +105,7 @@ public List annotations() { @Override public List symbolAnnotations() { if (symbolAnnotations == null) { - symbolAnnotations = Arrays.stream(symbolBindings) - .map(sema::annotation) - .collect(Collectors.toList()); + symbolAnnotations = annotationSource.get(); } return symbolAnnotations; } @@ -378,50 +338,4 @@ public Tree declaration() { } } - static final class JAnnotationInstance implements AnnotationInstance { - private final JSema sema; - private final IAnnotationBinding annotationBinding; - - /** - * Cache for {@link #values()}. - */ - private List values; - - JAnnotationInstance(JSema sema, IAnnotationBinding annotationBinding) { - this.sema = sema; - this.annotationBinding = annotationBinding; - } - - @Override - public Symbol symbol() { - return sema.typeSymbol(annotationBinding.getAnnotationType()); - } - - @Override - public List values() { - if (values == null) { - values = Arrays.stream(annotationBinding.getDeclaredMemberValuePairs()) - .map(p -> new AnnotationValueImpl(p.getName(), convertAnnotationValue(p.getValue()))) - .collect(Collectors.toList()); - } - return values; - } - - private Object convertAnnotationValue(Object value) { - return switch (value) { - case IVariableBinding iVariableBinding -> sema.variableSymbol(iVariableBinding); - case ITypeBinding iTypeBinding -> sema.typeSymbol(iTypeBinding); - case IAnnotationBinding iAnnotationBinding -> sema.annotation(iAnnotationBinding); - case Object[] a -> { - Object[] result = new Object[a.length]; - for (int i = 0; i < a.length; i++) { - result[i] = convertAnnotationValue(a[i]); - } - yield result; - } - case null, default -> value; - }; - } - } - } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JVariableSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JVariableSymbol.java index ae6458f39de..dd73f136563 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JVariableSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JVariableSymbol.java @@ -93,7 +93,7 @@ static class ParameterPlaceholderSymbol extends Symbols.DefaultSymbol implements this.name = "arg" + index; this.owner = sema.methodSymbol(owner); this.type = sema.type(typeBinding); - this.metadata = JSymbolMetadata.of(sema, this, typeBinding, owner.getParameterAnnotations(index)); + this.metadata = EcjSymbolMetadata.of(sema, this, typeBinding, owner.getParameterAnnotations(index)); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/JWarning.java b/java-frontend/src/main/java/org/sonar/java/model/JWarning.java index 8a95e692df6..bb995cd7d04 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JWarning.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JWarning.java @@ -28,9 +28,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.CheckForNull; -import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.core.dom.CompilationUnit; import org.sonar.java.annotations.VisibleForTesting; import org.sonar.java.ast.visitors.SubscriptionVisitor; import org.sonar.java.model.location.InternalPosition; @@ -97,30 +94,12 @@ public static class Mapper extends SubscriptionVisitor { private final PriorityQueue warnings = new PriorityQueue<>(Comparator.comparing(JWarning::start).thenComparing(JWarning::end)); - private Mapper(CompilationUnit ast, LineColumnConverter lineColumnConverter) { - Stream.of(ast.getProblems()) - .map(problem -> convert(problem, ast, lineColumnConverter)) - .filter(Objects::nonNull) - .forEach(warnings::add); + private Mapper(List diagnostics) { + warnings.addAll(diagnostics); } - @CheckForNull - private static JWarning convert(IProblem problem, CompilationUnit root, LineColumnConverter lineColumnConverter) { - for (Type type : Type.values()) { - if (type.matches(problem)) { - LineColumnConverter.Pos start = lineColumnConverter.toPos(problem.getSourceStart()); - LineColumnConverter.Pos end = lineColumnConverter.toPos(problem.getSourceStart()); - return new JWarning(problem.getMessage(), - type, - start.line(), start.columnOffset(), - end.line(), end.columnOffset()); - } - } - return null; - } - - public static Mapper warningsFor(CompilationUnit ast, LineColumnConverter lineColumnConverter) { - return new Mapper(ast, lineColumnConverter); + public static Mapper warningsFor(List diagnostics) { + return new Mapper(diagnostics); } public void mappedInto(JavaTree.CompilationUnitTreeImpl cut) { diff --git a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java index 95d1f03a49e..678c8127bad 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java @@ -90,7 +90,7 @@ void symbol_variable() { @Test void annotation() { - assertThat(countFields(JSymbolMetadata.JAnnotationInstance.class)).isEqualTo(3); + assertThat(countFields(EcjSymbolMetadata.JAnnotationInstance.class)).isEqualTo(3); } /** Count number of fields in instances, including inherited fields. */ diff --git a/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java b/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java index 60a2f838c2f..1597e9e24a5 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java @@ -32,6 +32,7 @@ import org.sonar.java.model.statement.ExpressionStatementTreeImpl; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; +import org.sonar.plugins.java.api.semantic.SymbolMetadata.AnnotationInstance; import org.sonar.plugins.java.api.semantic.SymbolMetadata.NullabilityLevel; import org.sonar.plugins.java.api.semantic.SymbolMetadata.NullabilityType; import org.sonar.plugins.java.api.tree.BaseTreeVisitor; @@ -44,6 +45,7 @@ import org.sonar.plugins.java.api.tree.Tree; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.sonar.plugins.java.api.semantic.SymbolMetadata.NullabilityLevel.CLASS; import static org.sonar.plugins.java.api.semantic.SymbolMetadata.NullabilityLevel.METHOD; import static org.sonar.plugins.java.api.semantic.SymbolMetadata.NullabilityLevel.PACKAGE; @@ -55,6 +57,21 @@ class JSymbolMetadataTest { + @Test + void flattened_annotations_are_independent_of_exposed_metadata_collections() { + Symbol owner = mock(Symbol.class); + AnnotationInstance outerAnnotation = mock(AnnotationInstance.class); + AnnotationInstance argumentAnnotation = mock(AnnotationInstance.class); + JSymbolMetadata argument = new JSymbolMetadata(owner, () -> new ArrayList<>(List.of(argumentAnnotation)), new JSymbolMetadata[0], true); + JSymbolMetadata[] parameters = {argument}; + JSymbolMetadata outer = new JSymbolMetadata(owner, () -> new ArrayList<>(List.of(outerAnnotation)), parameters, false); + argument.annotations().clear(); + argument.symbolAnnotations().clear(); + outer.symbolAnnotations().clear(); + parameters[0] = new JSymbolMetadata(owner, List.of()); + assertThat(outer.annotations()).containsExactly(outerAnnotation, argumentAnnotation); + } + private static final Path NULLABILITY_SOURCE_DIR = JParserTestUtils.CHECKS_TEST_DIR .resolve(Paths.get("src", "main", "java", "annotations", "nullability")); @@ -68,7 +85,7 @@ class JSymbolMetadataTest { void should_convert_annotation_values() { JavaTree.CompilationUnitTreeImpl cu = test("import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @interface A { }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); - Object[] value = (Object[]) cu.sema.typeSymbol(c.typeBinding).metadata().annotations().get(0).values().get(0).value(); + Object[] value = (Object[]) c.symbol().metadata().annotations().get(0).values().get(0).value(); assertThat(value).hasSize(2); assertThat(((JVariableSymbol) value[0]).isEnum()).isTrue(); assertThat(((JVariableSymbol) value[1]).isEnum()).isTrue(); diff --git a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java index 9a242880e5e..f66f5fd00c0 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java @@ -17,7 +17,6 @@ package org.sonar.java.model; import java.io.File; -import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.junit.jupiter.api.Test; import org.sonar.java.model.declaration.ClassTreeImpl; import org.sonar.java.model.declaration.VariableTreeImpl; @@ -104,7 +103,7 @@ void metadata_with_unknown_symbol_and_unsupported_declaration() { Tree unsupportedDeclaration = mock(Tree.class); Symbol symbol = spy(Symbol.UNKNOWN_SYMBOL); when(symbol.declaration()).thenReturn(unsupportedDeclaration); - SymbolMetadata metadata = new JSymbolMetadata(JUtilsTest.SEMA, symbol, new IAnnotationBinding[0]); + SymbolMetadata metadata = new JSymbolMetadata(symbol, java.util.List.of()); assertThat(metadata.findAnnotationTree(mock(AnnotationInstance.class))).isNull(); assertThat(metadata.nullabilityData().type()).isEqualTo(NullabilityType.UNKNOWN); assertThat(metadata.nullabilityData(NullabilityTarget.METHOD).type()).isEqualTo(NullabilityType.UNKNOWN); From ab39529d0bbff1afeb364faa1a5d8220fde24693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 2 Nov 2026 12:00:44 +0100 Subject: [PATCH 2/5] Remove compiler-specific casts from semantic helpers --- .../org/sonar/java/model/JMethodSymbol.java | 6 ++++ .../main/java/org/sonar/java/model/JType.java | 15 ++++++++++ .../org/sonar/java/model/JTypeSymbol.java | 14 ++++++++- .../java/org/sonar/java/model/JUtils.java | 29 ++----------------- .../java/org/sonar/java/model/Symbols.java | 5 ++++ .../plugins/java/api/semantic/Symbol.java | 9 ++++++ .../sonar/plugins/java/api/semantic/Type.java | 9 ++++++ .../java/org/sonar/java/model/JUtilsTest.java | 16 ++++++++++ .../org/sonar/java/model/SymbolsTest.java | 1 + 9 files changed, 77 insertions(+), 27 deletions(-) diff --git a/java-frontend/src/main/java/org/sonar/java/model/JMethodSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JMethodSymbol.java index 3f15b5f9a6b..4cdc7c88a87 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JMethodSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JMethodSymbol.java @@ -70,6 +70,12 @@ IMethodBinding methodBinding() { return (IMethodBinding) binding; } + @Override + @Nullable + public Object defaultValue() { + return EcjSymbolMetadata.convertAnnotationValue(sema, methodBinding().getDefaultValue()); + } + public boolean isLambda() { return methodBinding().getDeclaringMember() != null; } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JType.java b/java-frontend/src/main/java/org/sonar/java/model/JType.java index ee13c72eebe..caa8ba3daa0 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JType.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JType.java @@ -17,9 +17,11 @@ package org.sonar.java.model; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.TreeSet; import javax.annotation.Nullable; import org.eclipse.jdt.core.dom.ITypeBinding; @@ -63,6 +65,19 @@ final class JType implements Type, Type.ArrayType { this.fullyQualifiedName = fullyQualifiedName(typeBinding); } + @Override + public Set directSuperTypes() { + Set result = new HashSet<>(); + ITypeBinding superclass = typeBinding.getSuperclass(); + if (superclass != null) { + result.add(sema.type(superclass)); + } + Arrays.stream(typeBinding.getInterfaces()) + .map(sema::type) + .forEach(result::add); + return result; + } + @Override public boolean is(String fullyQualifiedName) { return fullyQualifiedName.equals(this.fullyQualifiedName()); diff --git a/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java index 0d41214e309..75f3c30bf89 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java @@ -162,12 +162,24 @@ public Set superTypes() { superTypes = Collections.emptySet(); } else { superTypes = new HashSet<>(); - JUtils.collectSuperTypes(superTypes, sema, typeBinding()); + collectSuperTypes(superTypes, sema, typeBinding()); } } return superTypes; } + private void collectSuperTypes(Set result, JSema sema, ITypeBinding typeBinding) { + ITypeBinding s = typeBinding.getSuperclass(); + if (s != null) { + result.add(sema.type(s)); + collectSuperTypes(result, sema, s); + } + Arrays.stream(typeBinding.getInterfaces()).forEach(i -> { + result.add(sema.type(i)); + collectSuperTypes(result, sema, i); + }); + } + @Override @Nullable public TypeSymbol outermostClass() { diff --git a/java-frontend/src/main/java/org/sonar/java/model/JUtils.java b/java-frontend/src/main/java/org/sonar/java/model/JUtils.java index 6018e221558..2419f7660c7 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JUtils.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JUtils.java @@ -16,7 +16,6 @@ */ package org.sonar.java.model; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; @@ -26,7 +25,6 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.semantic.Type; @@ -67,19 +65,7 @@ public static Type wrapTypeIfPrimitive(Type type) { } public static boolean isIntersectionType(Type type) { - return !type.isUnknown() && ((JType) type).typeBinding.isIntersectionType(); - } - - public static void collectSuperTypes(Set result, JSema sema, ITypeBinding typeBinding) { - ITypeBinding s = typeBinding.getSuperclass(); - if (s != null) { - result.add(sema.type(s)); - collectSuperTypes(result, sema, s); - } - Arrays.stream(typeBinding.getInterfaces()).forEach(i -> { - result.add(sema.type(i)); - collectSuperTypes(result, sema, i); - }); + return !type.isUnknown() && type.isIntersectionType(); } public static Symbol getPackage(Symbol symbol) { @@ -94,7 +80,7 @@ public static Object defaultValue(Symbol.MethodSymbol method) { if (method.isUnknown()) { return null; } - return ((JMethodSymbol) method).methodBinding().getDefaultValue(); + return method.defaultValue(); } /** @@ -161,16 +147,7 @@ public static Set directSuperTypes(Type type) { if (type.isUnknown()) { return Collections.emptySet(); } - Set result = new HashSet<>(); - JType t = (JType) type; - ITypeBinding superclass = t.typeBinding.getSuperclass(); - if (superclass != null) { - result.add(t.sema.type(superclass)); - } - Arrays.stream(t.typeBinding.getInterfaces()) - .map(t.sema::type) - .forEach(result::add); - return result; + return type.directSuperTypes(); } @Nullable diff --git a/java-frontend/src/main/java/org/sonar/java/model/Symbols.java b/java-frontend/src/main/java/org/sonar/java/model/Symbols.java index 767e1a9dbf2..6f78ab813df 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/Symbols.java +++ b/java-frontend/src/main/java/org/sonar/java/model/Symbols.java @@ -348,6 +348,11 @@ public boolean isMethodSymbol() { } public static final class UnknownType implements Type { + @Override + public Set directSuperTypes() { + return Collections.emptySet(); + } + @Override public boolean is(String fullyQualifiedName) { return false; diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Symbol.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Symbol.java index e17e57a418f..a88f3659bd4 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Symbol.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Symbol.java @@ -227,6 +227,15 @@ interface MethodSymbol extends Symbol { */ MethodSymbol UNKNOWN_METHOD = new Symbols.UnknownMethodSymbol(); + /** + * Returns the annotation member's default value, or {@code null} when none is available. Values use the + * same representation as {@link SymbolMetadata.AnnotationValue#value()}. + */ + @Nullable + default Object defaultValue() { + return null; + } + /** * Type of parameters declared by this method. * In case of generic types of method invocations, this list of types is more specialized than declarationParameters().stream().map(Symbol::type). diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Type.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Type.java index 0fe94a9a125..f7ddfa30c17 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Type.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Type.java @@ -17,6 +17,7 @@ package org.sonar.plugins.java.api.semantic; import java.util.List; +import java.util.Set; import javax.annotation.Nullable; import org.sonar.java.model.Symbols; @@ -30,6 +31,14 @@ public interface Type { */ Type UNKNOWN = new Symbols.UnknownType(); + /** + * Returns the immediate superclass and interfaces, preserving generic substitutions. Unknown types have + * no direct supertypes. Interfaces do not acquire an implicit {@code java.lang.Object} superclass. + */ + default Set directSuperTypes() { + return java.util.Collections.emptySet(); + } + /** * Primitive java types. */ diff --git a/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java index f586fda47a3..8a5fd6263b4 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java @@ -37,12 +37,28 @@ import org.sonar.plugins.java.api.tree.TypeParameterTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class JUtilsTest { static final JSema SEMA = test("").sema; private static final Type OBJECT_TYPE = SEMA.type(SEMA.resolveType("java.lang.Object")); + @Test + void semantic_helpers_use_the_existing_api_interfaces() { + Type type = mock(Type.class); + Type parent = mock(Type.class); + when(type.directSuperTypes()).thenReturn(Set.of(parent)); + assertThat(JUtils.directSuperTypes(type)).containsExactly(parent); + Symbol.MethodSymbol method = mock(Symbol.MethodSymbol.class); + when(method.defaultValue()).thenReturn(42); + assertThat(JUtils.defaultValue(method)).isEqualTo(42); + assertThat(Type.UNKNOWN.directSuperTypes()).isEmpty(); + assertThat(Symbol.MethodSymbol.UNKNOWN_METHOD.defaultValue()).isNull(); + assertThat(new JInitializerBlockSymbol(Symbol.TypeSymbol.UNKNOWN_TYPE, false).defaultValue()).isNull(); + } + @Nested class PrimitiveType { @Test diff --git a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java index f66f5fd00c0..7451d522b5b 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java @@ -43,6 +43,7 @@ void unknown_type() { Type unknownType = Type.UNKNOWN; assertThat(unknownType.isUnknown()).isTrue(); + assertThat(unknownType.directSuperTypes()).isEmpty(); assertThat(unknownType).isEqualTo(Type.UNKNOWN); assertThat(unknownType.is("!Unknown!")).isFalse(); From bf7fd58a62e5a83ab14539f7bd1054fbd69f9266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 2 Nov 2026 12:00:53 +0100 Subject: [PATCH 3/5] Replace shared-tree compiler bindings with lazy semantic values --- .../sonar/java/checks/DeadStoreCheckTest.java | 3 +- .../org/sonar/java/ast/JavaAstScanner.java | 2 +- .../sonar/java/model/AbstractTypedTree.java | 11 +- .../java/model/EcjSemanticAttachment.java | 48 ++ .../org/sonar/java/model/EcjTreeBindings.java | 103 ++++ .../java/org/sonar/java/model/JParser.java | 217 ++++---- .../main/java/org/sonar/java/model/JSema.java | 30 +- .../java/org/sonar/java/model/JSymbol.java | 5 +- .../org/sonar/java/model/JTypeSymbol.java | 2 +- .../java/org/sonar/java/model/JavaTree.java | 29 +- .../org/sonar/java/model/KeywordSuper.java | 15 +- .../org/sonar/java/model/KeywordThis.java | 15 +- .../org/sonar/java/model/SemanticValues.java | 33 ++ .../java/model/TypeParameterTreeImpl.java | 9 +- .../java/model/declaration/ClassTreeImpl.java | 10 +- .../model/declaration/MethodTreeImpl.java | 10 +- .../model/declaration/VariableTreeImpl.java | 9 +- .../model/expression/IdentifierTreeImpl.java | 23 +- .../expression/LambdaExpressionTreeImpl.java | 10 +- .../expression/MethodInvocationTreeImpl.java | 10 +- .../model/pattern/AbstractPatternTree.java | 14 +- .../model/pattern/DefaultPatternTreeImpl.java | 7 +- .../model/pattern/GuardedPatternTreeImpl.java | 7 +- .../model/pattern/TypePatternTreeImpl.java | 7 +- .../sonar/plugins/java/api/semantic/Sema.java | 8 + .../org/sonar/java/SonarComponentsTest.java | 12 +- .../sonar/java/ast/JavaAstScannerTest.java | 3 +- .../sonar/java/model/ClassesLayoutTest.java | 21 + .../org/sonar/java/model/EcjTestSupport.java | 77 +++ .../sonar/java/model/JMethodSymbolTest.java | 53 +- .../sonar/java/model/JParserSemanticTest.java | 499 +++++++++--------- .../org/sonar/java/model/JParserTest.java | 4 +- .../org/sonar/java/model/JSymbolTest.java | 146 ++--- .../org/sonar/java/model/JTypeSymbolTest.java | 80 +-- .../java/org/sonar/java/model/JTypeTest.java | 30 +- .../java/org/sonar/java/model/JUtilsTest.java | 43 +- .../sonar/java/model/JVariableSymbolTest.java | 10 +- .../sonar/java/model/KeywordSuperTest.java | 6 +- .../org/sonar/java/model/KeywordThisTest.java | 6 +- .../org/sonar/java/model/SymbolsTest.java | 3 +- .../model/declaration/ClassTreeImplTest.java | 3 +- 41 files changed, 984 insertions(+), 649 deletions(-) create mode 100644 java-frontend/src/main/java/org/sonar/java/model/EcjSemanticAttachment.java create mode 100644 java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java create mode 100644 java-frontend/src/main/java/org/sonar/java/model/SemanticValues.java create mode 100644 java-frontend/src/test/java/org/sonar/java/model/EcjTestSupport.java diff --git a/java-checks/src/test/java/org/sonar/java/checks/DeadStoreCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/DeadStoreCheckTest.java index a539841b022..857b1b0d745 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/DeadStoreCheckTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/DeadStoreCheckTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.sonar.java.checks.verifier.CheckVerifier; import org.sonar.java.checks.verifier.TestUtils; +import org.sonar.java.model.SemanticValues; import org.sonar.java.model.declaration.VariableTreeImpl; import org.sonar.plugins.java.api.tree.BaseTreeVisitor; import org.sonar.plugins.java.api.tree.VariableTree; @@ -64,7 +65,7 @@ private static class EraseSymbols extends BaseTreeVisitor { @Override public void visitVariable(VariableTree tree) { if (tree instanceof VariableTreeImpl varImpl) { - varImpl.variableBinding = null; + varImpl.variableSymbol = SemanticValues.UNKNOWN_SYMBOL; } super.visitVariable(tree); } diff --git a/java-frontend/src/main/java/org/sonar/java/ast/JavaAstScanner.java b/java-frontend/src/main/java/org/sonar/java/ast/JavaAstScanner.java index fcd39ab2a21..cbfb0b41964 100644 --- a/java-frontend/src/main/java/org/sonar/java/ast/JavaAstScanner.java +++ b/java-frontend/src/main/java/org/sonar/java/ast/JavaAstScanner.java @@ -166,7 +166,7 @@ public void simpleScan(InputFile inputFile, JParserConfig.Result result, Consume modifyCompilationUnit.accept(ast); visitor.visitFile(ast, sonarComponents != null && sonarComponents.fileCanBeSkipped(inputFile)); String path = inputFile.toString(); - Set undefinedTypes = ast.sema.undefinedTypes(); + Set undefinedTypes = ast.undefinedTypes(); collectUndefinedTypes(path, undefinedTypes); cleanUp.accept(ast); telemetryAnalysisKeys = javaAnalysisKeys.success(); diff --git a/java-frontend/src/main/java/org/sonar/java/model/AbstractTypedTree.java b/java-frontend/src/main/java/org/sonar/java/model/AbstractTypedTree.java index f2d6def6b5e..8633318e58b 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/AbstractTypedTree.java +++ b/java-frontend/src/main/java/org/sonar/java/model/AbstractTypedTree.java @@ -16,23 +16,18 @@ */ package org.sonar.java.model; -import org.eclipse.jdt.core.dom.ITypeBinding; +import java.util.function.Supplier; import org.sonar.plugins.java.api.semantic.Type; -import javax.annotation.Nullable; - /** * This class is intended for internal use during semantic analysis and should not be used in checks. */ public abstract class AbstractTypedTree extends JavaTree { - @Nullable - public ITypeBinding typeBinding; + public Supplier type = SemanticValues.UNKNOWN_TYPE; public Type symbolType() { - return typeBinding != null - ? root.sema.type(typeBinding) - : Type.UNKNOWN; + return type.get(); } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjSemanticAttachment.java b/java-frontend/src/main/java/org/sonar/java/model/EcjSemanticAttachment.java new file mode 100644 index 00000000000..4b586b9989b --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjSemanticAttachment.java @@ -0,0 +1,48 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import org.eclipse.jdt.core.dom.IBinding; + +final class EcjSemanticAttachment implements Supplier { + + final B binding; + private final Function factory; + private T value; + + EcjSemanticAttachment(B binding, Function factory) { + this.binding = binding; + this.factory = factory; + } + + @Override + public T get() { + if (value == null) { + value = Objects.requireNonNull(factory.apply(binding)); + } + return value; + } + + @Nullable + static IBinding getBinding(Supplier attachment) { + return attachment instanceof EcjSemanticAttachment ecj ? ecj.binding : null; + } +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java b/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java new file mode 100644 index 00000000000..045d24b4eac --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java @@ -0,0 +1,103 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import java.util.function.Supplier; +import javax.annotation.Nullable; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.sonar.java.model.declaration.ClassTreeImpl; +import org.sonar.java.model.declaration.MethodTreeImpl; +import org.sonar.java.model.declaration.VariableTreeImpl; +import org.sonar.java.model.expression.IdentifierTreeImpl; +import org.sonar.java.model.expression.LambdaExpressionTreeImpl; +import org.sonar.java.model.expression.MethodInvocationTreeImpl; +import org.sonar.java.model.pattern.AbstractPatternTree; + +final class EcjTreeBindings { + + private EcjTreeBindings() { + } + + @Nullable + static ITypeBinding typeBinding(Object tree) { + return switch (tree) { + case AbstractTypedTree t -> (ITypeBinding) EcjSemanticAttachment.getBinding(t.type); + case ClassTreeImpl t -> (ITypeBinding) EcjSemanticAttachment.getBinding(t.typeSymbol); + case TypeParameterTreeImpl t -> (ITypeBinding) EcjSemanticAttachment.getBinding(t.typeSymbol); + case AbstractPatternTree t -> (ITypeBinding) EcjSemanticAttachment.getBinding(t.type); + default -> throw new IllegalArgumentException("No ECJ type attachment: " + tree.getClass()); + }; + } + + @Nullable + static IMethodBinding methodBinding(Object tree) { + Supplier attachment = switch (tree) { + case MethodTreeImpl t -> t.methodSymbol; + case MethodInvocationTreeImpl t -> t.methodSymbolSupplier; + case LambdaExpressionTreeImpl t -> t.methodSymbol; + default -> throw new IllegalArgumentException("No ECJ method attachment: " + tree.getClass()); + }; + return (IMethodBinding) EcjSemanticAttachment.getBinding(attachment); + } + + @Nullable + static IVariableBinding variableBinding(VariableTreeImpl tree) { + return (IVariableBinding) EcjSemanticAttachment.getBinding(tree.variableSymbol); + } + + @Nullable + static IBinding binding(Object tree) { + return switch (tree) { + case IdentifierTreeImpl t -> EcjSemanticAttachment.getBinding(t.symbolSupplier); + case JavaTree.ImportTreeImpl t -> EcjSemanticAttachment.getBinding(t.symbolSupplier); + default -> throw new IllegalArgumentException("No ECJ symbol attachment: " + tree.getClass()); + }; + } + + static void setType(JSema sema, JavaTree tree, @Nullable ITypeBinding binding) { + switch (tree) { + case AbstractTypedTree t -> t.type = sema.typeSupplier(binding); + case ClassTreeImpl t -> t.typeSymbol = binding == null ? SemanticValues.UNKNOWN_TYPE_SYMBOL + : new EcjSemanticAttachment<>(binding, sema::typeSymbol); + case TypeParameterTreeImpl t -> t.typeSymbol = sema.symbolSupplier(binding); + default -> throw new IllegalArgumentException("No type attachment: " + tree.getClass()); + } + if (tree instanceof KeywordThis t) { + t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL + : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).thisSymbol); + } else if (tree instanceof KeywordSuper t) { + t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL + : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).superSymbol); + } + } + + static void setSymbol(JSema sema, JavaTree tree, @Nullable IBinding binding) { + switch (tree) { + case MethodTreeImpl t -> t.methodSymbol = sema.methodSupplier((IMethodBinding) binding); + case MethodInvocationTreeImpl t -> t.methodSymbolSupplier = sema.methodSupplier((IMethodBinding) binding); + case LambdaExpressionTreeImpl t -> t.methodSymbol = sema.methodSupplier((IMethodBinding) binding); + case VariableTreeImpl t -> t.variableSymbol = sema.symbolSupplier(binding); + case IdentifierTreeImpl t -> t.symbolSupplier = sema.symbolSupplier(binding); + case JavaTree.ImportTreeImpl t -> t.symbolSupplier = binding == null || binding.getKind() == IBinding.PACKAGE + || binding.getKind() == IBinding.MODULE ? () -> null : sema.symbolSupplier(binding); + default -> throw new IllegalArgumentException("No symbol attachment: " + tree.getClass()); + } + } +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/JParser.java b/java-frontend/src/main/java/org/sonar/java/model/JParser.java index cd47482e4cd..6ddf24dee0d 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JParser.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JParser.java @@ -252,9 +252,34 @@ import org.sonar.plugins.java.api.tree.TypeTree; import org.sonar.plugins.java.api.tree.VariableTree; +import static org.sonar.java.model.EcjTreeBindings.binding; +import static org.sonar.java.model.EcjTreeBindings.methodBinding; +import static org.sonar.java.model.EcjTreeBindings.typeBinding; +import static org.sonar.java.model.EcjTreeBindings.variableBinding; + @SuppressWarnings({"rawtypes", "unchecked"}) public class JParser { + private void setType(JavaTree tree, @Nullable ITypeBinding binding) { + EcjTreeBindings.setType(sema, tree, binding); + } + + private void setSymbol(JavaTree tree, @Nullable IBinding binding) { + EcjTreeBindings.setSymbol(sema, tree, binding); + } + + private KeywordThis keywordThis(InternalSyntaxToken token, @Nullable ITypeBinding binding) { + KeywordThis tree = new KeywordThis(token); + setType(tree, binding); + return tree; + } + + private KeywordSuper keywordSuper(InternalSyntaxToken token, @Nullable ITypeBinding binding) { + KeywordSuper tree = new KeywordSuper(token); + setType(tree, binding); + return tree; + } + private static final Logger LOG = LoggerFactory.getLogger(JParser.class); private static final Set WRONGLY_CATEGORIZED_AS_SYNTAX_ERROR = Set.of( @@ -312,13 +337,13 @@ static JavaTree.CompilationUnitTreeImpl convert(String version, String unitName, JParser converter = new JParser(); converter.sema = new JSema(astNode.getAST()); - converter.sema.undefinedTypes.addAll(undefinedTypes); converter.compilationUnit = astNode; converter.tokenManager = createTokenManager(version, unitName, source); converter.lineColumnConverter = lineColumnConverter; JavaTree.CompilationUnitTreeImpl tree = converter.convertCompilationUnit(astNode); tree.sema = converter.sema; + tree.undefinedTypes.addAll(undefinedTypes); JWarning.Mapper.warningsFor(EcjDiagnosticProvider.warnings(astNode, converter.lineColumnConverter)).mappedInto(tree); ASTUtils.mayTolerateMissingType(astNode.getAST()); @@ -598,7 +623,7 @@ private JavaTree.CompilationUnitTreeImpl convertCompilationUnit(CompilationUnit if (!isModuleImport) { // There is no method to resolve bindings for a module import. - t.binding = e2.resolveBinding(); + setSymbol(t, e2.resolveBinding()); } imports.add(t); @@ -776,8 +801,8 @@ private ClassTreeImpl convertUnnamedClassDeclaration(AbstractTypeDeclaration e) ClassTreeImpl t = new ClassTreeImpl(Tree.Kind.IMPLICIT_CLASS, null, members, null); - t.typeBinding = e.resolveBinding(); - declaration(t.typeBinding, t); + setType(t, e.resolveBinding()); + declaration(typeBinding(t), t); return t; } @@ -823,8 +848,8 @@ private ClassTreeImpl convertTypeDeclaration(AbstractTypeDeclaration e) { // no-op for annotation-types completeSuperInterfaces(e, t); - t.typeBinding = e.resolveBinding(); - declaration(t.typeBinding, t); + setType(t, e.resolveBinding()); + declaration(typeBinding(t), t); return t; } @@ -995,8 +1020,8 @@ private EnumConstantTreeImpl processEnumConstantDeclaration(EnumConstantDeclarat members, lastTokenIn(e.getAnonymousClassDeclaration(), TerminalToken.TokenNameRBRACE) ); - classBody.typeBinding = e.getAnonymousClassDeclaration().resolveBinding(); - declaration(classBody.typeBinding, classBody); + setType(classBody, e.getAnonymousClassDeclaration().resolveBinding()); + declaration(typeBinding(classBody), classBody); } final int separatorTokenIndex = firstTokenIndexAfter(e); @@ -1015,11 +1040,11 @@ private EnumConstantTreeImpl processEnumConstantDeclaration(EnumConstantDeclarat IdentifierTreeImpl identifier = createSimpleName(e.getName()); if (e.getAnonymousClassDeclaration() == null) { - identifier.binding = excludeRecovery(e.resolveConstructorBinding(), arguments.size()); + setSymbol(identifier, excludeRecovery(e.resolveConstructorBinding(), arguments.size())); } else { - identifier.binding = findConstructorForAnonymousClass(e.getAST(), identifier.typeBinding, e.resolveConstructorBinding()); + setSymbol(identifier, findConstructorForAnonymousClass(e.getAST(), typeBinding(identifier), e.resolveConstructorBinding())); } - usage(identifier.binding, identifier); + usage(binding(identifier), identifier); EnumConstantTreeImpl t = new EnumConstantTreeImpl( convertModifiers(e.modifiers()), @@ -1027,8 +1052,8 @@ private EnumConstantTreeImpl processEnumConstantDeclaration(EnumConstantDeclarat new NewClassTreeImpl(identifier, arguments, classBody), separatorToken ); - t.variableBinding = e.resolveVariable(); - declaration(t.variableBinding, t); + setSymbol(t, e.resolveVariable()); + declaration(variableBinding(t), t); return t; } @@ -1080,8 +1105,8 @@ private int processAnnotationTypeMemberDeclaration(AnnotationTypeMemberDeclarati .complete(convertType(e.getType()), createSimpleName(e.getName()), lastTokenIn(e, TerminalToken.TokenNameSEMICOLON)) .completeWithModifiers(convertModifiers(e.modifiers())); - t.methodBinding = e.resolveBinding(); - declaration(t.methodBinding, t); + setSymbol(t, e.resolveBinding()); + declaration(methodBinding(t), t); members.add(t); return tokenManager.lastIndexIn(e, TerminalToken.TokenNameSEMICOLON); @@ -1147,8 +1172,8 @@ private int processMethodDeclaration(MethodDeclaration e, List members) { ).completeWithTypeParameters( convertTypeParameters(e.typeParameters()) ); - t.methodBinding = e.resolveBinding(); - declaration(t.methodBinding, t); + setSymbol(t, e.resolveBinding()); + declaration(methodBinding(t), t); members.add(t); return tokenManager.lastIndexIn(e, body == null ? TerminalToken.TokenNameSEMICOLON : TerminalToken.TokenNameRBRACE); @@ -1168,8 +1193,8 @@ private int processFieldDeclaration(FieldDeclaration fieldDeclaration, List extraDimensions) { - ITypeBinding typeBinding = ((AbstractTypedTree) type).typeBinding; + ITypeBinding typeBinding = typeBinding(type); for (int i = 0; i < extraDimensions.size(); i++) { Dimension e = (Dimension) extraDimensions.get(i); type = new JavaTree.ArrayTypeTreeImpl( @@ -1283,7 +1308,7 @@ private TypeTree applyExtraDimensions(TypeTree type, List extraDimensions) { firstTokenIn(e, TerminalToken.TokenNameRBRACKET) ); if (typeBinding != null) { - ((JavaTree.ArrayTypeTreeImpl) type).typeBinding = typeBinding.createArrayType(i + 1); + setType(((JavaTree.ArrayTypeTreeImpl) type), typeBinding.createArrayType(i + 1)); } } return type; @@ -1312,14 +1337,14 @@ private VariableTreeImpl convertVariable(SingleVariableDeclaration e) { TypeTree type = convertType(e.getType()); type = applyExtraDimensions(type, e.extraDimensions()); if (e.isVarargs()) { - ITypeBinding typeBinding = ((AbstractTypedTree) type).typeBinding; + ITypeBinding typeBinding = typeBinding(type); type = new JavaTree.ArrayTypeTreeImpl( type, (List) convertAnnotations(e.varargsAnnotations()), firstTokenAfter(e.getType(), TerminalToken.TokenNameELLIPSIS) ); if (typeBinding != null) { - ((JavaTree.ArrayTypeTreeImpl) type).typeBinding = typeBinding.createArrayType(1); + setType(((JavaTree.ArrayTypeTreeImpl) type), typeBinding.createArrayType(1)); } } @@ -1339,12 +1364,12 @@ private VariableTreeImpl completeInitializerAndBinding(VariableTreeImpl t, Varia convertExpression(e.getInitializer()) ); } - t.variableBinding = e.resolveBinding(); - if (t.variableBinding != null) { + setSymbol(t, e.resolveBinding()); + if (variableBinding(t) != null) { if (t.type() instanceof InferedTypeTree inferredType) { - inferredType.typeBinding = t.variableBinding.getType(); + setType(inferredType, variableBinding(t).getType()); } - declaration(t.variableBinding, t); + declaration(variableBinding(t), t); } return t; } @@ -1369,15 +1394,15 @@ private void addVariableToList(VariableDeclarationExpression e2, List list) { if (i < e2.fragments().size() - 1) { t.setEndToken(firstTokenAfter(fragment, TerminalToken.TokenNameCOMMA)); } - t.variableBinding = fragment.resolveBinding(); - declaration(t.variableBinding, t); + setSymbol(t, fragment.resolveBinding()); + declaration(variableBinding(t), t); list.add(t); } } private VarTypeTreeImpl convertVarType(SimpleType simpleType) { VarTypeTreeImpl varTree = new VarTypeTreeImpl(firstTokenIn(simpleType.getName(), TerminalToken.TokenNameIdentifier)); - varTree.typeBinding = simpleType.resolveBinding(); + setType(varTree, simpleType.resolveBinding()); return varTree; } @@ -1386,8 +1411,8 @@ private IdentifierTreeImpl createSimpleName(SimpleName e) { Token token = tokenManager.get(tokenIndex); boolean isUnnamedVariable = token.tokenType == TerminalToken.TokenNameUNDERSCORE; IdentifierTreeImpl t = new IdentifierTreeImpl(createSyntaxToken(tokenIndex), isUnnamedVariable); - t.typeBinding = e.resolveTypeBinding(); - t.binding = e.resolveBinding(); + setType(t, e.resolveTypeBinding()); + setSymbol(t, e.resolveBinding()); return t; } @@ -1421,8 +1446,8 @@ private void addStatementToList(Statement node, List statements) TerminalToken endTokenType = i < e.fragments().size() - 1 ? TerminalToken.TokenNameCOMMA : TerminalToken.TokenNameSEMICOLON; t.setEndToken(firstTokenAfter(fragment, endTokenType)); - t.variableBinding = fragment.resolveBinding(); - declaration(t.variableBinding, t); + setSymbol(t, fragment.resolveBinding()); + declaration(variableBinding(t), t); statements.add(t); } } else { @@ -1650,7 +1675,7 @@ private List convertSwitchStatements(List list) { private ExpressionTree convertExpressionFromCase(Expression e) { if (e.getNodeType() == ASTNode.CASE_DEFAULT_EXPRESSION) { - return new DefaultPatternTreeImpl(firstTokenIn(e, TerminalToken.TokenNamedefault), e.resolveTypeBinding()); + return new DefaultPatternTreeImpl(firstTokenIn(e, TerminalToken.TokenNamedefault), sema.typeSupplier(e.resolveTypeBinding())); } if (e.getNodeType() == ASTNode.NULL_LITERAL) { return new NullPatternTreeImpl((LiteralTreeImpl) convertExpression(e)); @@ -1665,7 +1690,7 @@ private PatternTree convertPattern(Pattern p) { switch (p.getNodeType()) { case ASTNode.TYPE_PATTERN: TypePattern typePattern = (TypePattern) p; - return new TypePatternTreeImpl(convertVariable(typePattern), typePattern.resolveTypeBinding()); + return new TypePatternTreeImpl(convertVariable(typePattern), sema.typeSupplier(typePattern.resolveTypeBinding())); case ASTNode.RECORD_PATTERN: RecordPattern recordPattern = (RecordPattern) p; List nestedPatterns = recordPattern.patterns().stream() @@ -1682,7 +1707,7 @@ private PatternTree convertPattern(Pattern p) { convertPattern(g.getPattern()), firstTokenBefore(g.getExpression(), TerminalToken.TokenNameRestrictedIdentifierWhen), convertExpression(g.getExpression()), - g.resolveTypeBinding()); + sema.typeSupplier(g.resolveTypeBinding())); case ASTNode.NULL_PATTERN: // It is not clear how to reach this one, it seems to be possible only with badly constructed AST // fall-through. Do nothing for now. @@ -1843,13 +1868,13 @@ private ExpressionStatementTreeImpl convertConstructorInvocation(ConstructorInvo convertTypeArguments(e.typeArguments()), arguments ); - t.methodBinding = e.resolveConstructorBinding(); - if (t.methodBinding != null) { - t.typeBinding = t.methodBinding.getDeclaringClass(); - t.methodBinding = excludeRecovery(t.methodBinding, arguments.size()); + setSymbol(t, e.resolveConstructorBinding()); + if (methodBinding(t) != null) { + setType(t, methodBinding(t).getDeclaringClass()); + setSymbol(t, excludeRecovery(methodBinding(t), arguments.size())); } - i.binding = t.methodBinding; - usage(i.binding, i); + setSymbol(i, methodBinding(t)); + usage(binding(i), i); return new ExpressionStatementTreeImpl( t, lastTokenIn(e, TerminalToken.TokenNameSEMICOLON) @@ -1878,13 +1903,13 @@ private ExpressionStatementTreeImpl convertSuperConstructorInvocation(SuperConst convertTypeArguments(e.typeArguments()), arguments ); - t.methodBinding = e.resolveConstructorBinding(); - if (t.methodBinding != null) { - t.typeBinding = t.methodBinding.getDeclaringClass(); - t.methodBinding = excludeRecovery(t.methodBinding, arguments.size()); + setSymbol(t, e.resolveConstructorBinding()); + if (methodBinding(t) != null) { + setType(t, methodBinding(t).getDeclaringClass()); + setSymbol(t, excludeRecovery(methodBinding(t), arguments.size())); } - i.binding = t.methodBinding; - usage(i.binding, i); + setSymbol(i, methodBinding(t)); + usage(binding(i), i); return new ExpressionStatementTreeImpl( t, lastTokenIn(e, TerminalToken.TokenNameSEMICOLON) @@ -1905,7 +1930,7 @@ private YieldStatementTreeImpl convertYield(YieldStatement e) { private ExpressionTree convertExpression(Expression node) { ExpressionTree t = createExpression(node); - ((AbstractTypedTree) t).typeBinding = node.resolveTypeBinding(); + setType(((AbstractTypedTree) t), node.resolveTypeBinding()); return t; } @@ -1988,13 +2013,13 @@ private ExpressionTree createExpression(Expression node) { private IdentifierTreeImpl convertSimpleName(SimpleName e) { IdentifierTreeImpl t = createSimpleName(e); - usage(t.binding, t); + usage(binding(t), t); return t; } private MemberSelectExpressionTreeImpl convertQualifiedName(QualifiedName e) { IdentifierTreeImpl rhs = createSimpleName(e.getName()); - usage(rhs.binding, rhs); + usage(binding(rhs), rhs); return new MemberSelectExpressionTreeImpl( convertExpression(e.getQualifier()), firstTokenAfter(e.getQualifier(), TerminalToken.TokenNameDOT), @@ -2004,7 +2029,7 @@ private MemberSelectExpressionTreeImpl convertQualifiedName(QualifiedName e) { private MemberSelectExpressionTreeImpl convertFieldAccess(FieldAccess e) { IdentifierTreeImpl rhs = createSimpleName(e.getName()); - usage(rhs.binding, rhs); + usage(binding(rhs), rhs); return new MemberSelectExpressionTreeImpl( convertExpression(e.getExpression()), firstTokenAfter(e.getExpression(), TerminalToken.TokenNameDOT), @@ -2014,7 +2039,7 @@ private MemberSelectExpressionTreeImpl convertFieldAccess(FieldAccess e) { private MemberSelectExpressionTreeImpl convertFieldAccess(SuperFieldAccess e) { IdentifierTreeImpl rhs = createSimpleName(e.getName()); - usage(rhs.binding, rhs); + usage(binding(rhs), rhs); if (e.getQualifier() == null) { // super.name return new MemberSelectExpressionTreeImpl( @@ -2025,15 +2050,15 @@ private MemberSelectExpressionTreeImpl convertFieldAccess(SuperFieldAccess e) { } // qualifier.super.name AbstractTypedTree qualifier = (AbstractTypedTree) convertExpression(e.getQualifier()); - KeywordSuper keywordSuper = new KeywordSuper(firstTokenAfter(e.getQualifier(), TerminalToken.TokenNamesuper), null); + KeywordSuper keywordSuper = keywordSuper(firstTokenAfter(e.getQualifier(), TerminalToken.TokenNamesuper), null); MemberSelectExpressionTreeImpl qualifiedSuper = new MemberSelectExpressionTreeImpl( (ExpressionTree) qualifier, firstTokenAfter(e.getQualifier(), TerminalToken.TokenNameDOT), keywordSuper ); - if (qualifier.typeBinding != null) { - keywordSuper.typeBinding = qualifier.typeBinding; - qualifiedSuper.typeBinding = keywordSuper.typeBinding.getSuperclass(); + if (typeBinding(qualifier) != null) { + setType(keywordSuper, typeBinding(qualifier)); + setType(qualifiedSuper, typeBinding(keywordSuper).getSuperclass()); } return new MemberSelectExpressionTreeImpl( qualifiedSuper, @@ -2044,9 +2069,9 @@ private MemberSelectExpressionTreeImpl convertFieldAccess(SuperFieldAccess e) { private ExpressionTree convertThisExpression(ThisExpression e) { if (e.getQualifier() == null) { - return new KeywordThis(firstTokenIn(e, TerminalToken.TokenNamethis), null); + return keywordThis(firstTokenIn(e, TerminalToken.TokenNamethis), null); } - KeywordThis keywordThis = new KeywordThis( + KeywordThis keywordThis = keywordThis( firstTokenAfter(e.getQualifier(), TerminalToken.TokenNamethis), e.resolveTypeBinding() ); @@ -2200,8 +2225,8 @@ private NewClassTreeImpl convertClassInstanceCreation(ClassInstanceCreation e) { members, lastTokenIn(e.getAnonymousClassDeclaration(), TerminalToken.TokenNameRBRACE) ); - classBody.typeBinding = e.getAnonymousClassDeclaration().resolveBinding(); - declaration(classBody.typeBinding, classBody); + setType(classBody, e.getAnonymousClassDeclaration().resolveBinding()); + declaration(typeBinding(classBody), classBody); } NewClassTreeImpl t = new NewClassTreeImpl( @@ -2221,11 +2246,11 @@ private NewClassTreeImpl convertClassInstanceCreation(ClassInstanceCreation e) { IdentifierTreeImpl i = (IdentifierTreeImpl) t.getConstructorIdentifier(); int nbArguments = arguments.size(); if (e.getAnonymousClassDeclaration() == null) { - i.binding = excludeRecovery(e.resolveConstructorBinding(), nbArguments); + setSymbol(i, excludeRecovery(e.resolveConstructorBinding(), nbArguments)); } else { - i.binding = excludeRecovery(findConstructorForAnonymousClass(e.getAST(), i.typeBinding, e.resolveConstructorBinding()), nbArguments); + setSymbol(i, excludeRecovery(findConstructorForAnonymousClass(e.getAST(), typeBinding(i), e.resolveConstructorBinding()), nbArguments)); } - usage(i.binding, i); + usage(binding(i), i); return t; } @@ -2250,7 +2275,7 @@ private BinaryExpressionTreeImpl convertInfixExpression(InfixExpression e) { ); for (Object o : e.extendedOperands()) { Expression e2 = (Expression) o; - t.typeBinding = e.resolveTypeBinding(); + setType(t, e.resolveTypeBinding()); t = new BinaryExpressionTreeImpl( op.kind, t, @@ -2284,9 +2309,9 @@ private MethodInvocationTreeImpl convertMethodInvocation(MethodInvocation e) { convertTypeArguments(e.typeArguments()), arguments ); - t.methodBinding = excludeRecovery(e.resolveMethodBinding(), arguments.size()); - rhs.binding = t.methodBinding; - usage(rhs.binding, rhs); + setSymbol(t, excludeRecovery(e.resolveMethodBinding(), arguments.size())); + setSymbol(rhs, methodBinding(t)); + usage(binding(rhs), rhs); return t; } @@ -2309,15 +2334,15 @@ private MethodInvocationTreeImpl convertMethodInvocation(SuperMethodInvocation e } else { final int firstDotTokenIndex = tokenManager.firstIndexAfter(e.getQualifier(), TerminalToken.TokenNameDOT); AbstractTypedTree qualifier = (AbstractTypedTree) convertExpression(e.getQualifier()); - KeywordSuper keywordSuper = new KeywordSuper(firstTokenAfter(e.getQualifier(), TerminalToken.TokenNamesuper), null); + KeywordSuper keywordSuper = keywordSuper(firstTokenAfter(e.getQualifier(), TerminalToken.TokenNamesuper), null); MemberSelectExpressionTreeImpl qualifiedSuper = new MemberSelectExpressionTreeImpl( (ExpressionTree) qualifier, createSyntaxToken(firstDotTokenIndex), keywordSuper ); - if (qualifier.typeBinding != null) { - keywordSuper.typeBinding = qualifier.typeBinding; - qualifiedSuper.typeBinding = keywordSuper.typeBinding.getSuperclass(); + if (typeBinding(qualifier) != null) { + setType(keywordSuper, typeBinding(qualifier)); + setType(qualifiedSuper, typeBinding(keywordSuper).getSuperclass()); } outermostSelect = new MemberSelectExpressionTreeImpl( qualifiedSuper, @@ -2331,9 +2356,9 @@ private MethodInvocationTreeImpl convertMethodInvocation(SuperMethodInvocation e null, arguments ); - t.methodBinding = excludeRecovery(e.resolveMethodBinding(), arguments.size()); - rhs.binding = t.methodBinding; - usage(rhs.binding, rhs); + setSymbol(t, excludeRecovery(e.resolveMethodBinding(), arguments.size())); + setSymbol(rhs, methodBinding(t)); + usage(binding(rhs), rhs); return t; } @@ -2384,9 +2409,9 @@ private LambdaExpressionTreeImpl convertLambdaExpression(LambdaExpression e) { t = new VariableTreeImpl(createSimpleName(o.getName())); IVariableBinding variableBinding = o.resolveBinding(); if (variableBinding != null) { - t.variableBinding = variableBinding; - ((InferedTypeTree) t.type()).typeBinding = variableBinding.getType(); - declaration(t.variableBinding, t); + setSymbol(t, variableBinding); + setType(((InferedTypeTree) t.type()), variableBinding.getType()); + declaration(variableBinding(t), t); } } else { t = convertVariable((SingleVariableDeclaration) o); @@ -2404,7 +2429,7 @@ private LambdaExpressionTreeImpl convertLambdaExpression(LambdaExpression e) { firstTokenBefore(body, TerminalToken.TokenNameARROW), body.getNodeType() == ASTNode.BLOCK ? convertBlock((Block) body) : convertExpression((Expression) body) ); - tree.methodBinding = e.resolveMethodBinding(); + setSymbol(tree, e.resolveMethodBinding()); return tree; } @@ -2414,8 +2439,8 @@ private MethodReferenceTreeImpl convertMethodReference(CreationReference e) { lastTokenIn(e, TerminalToken.TokenNameCOLON_COLON) ); IdentifierTreeImpl i = new IdentifierTreeImpl(lastTokenIn(e, TerminalToken.TokenNamenew)); - i.binding = e.resolveMethodBinding(); - usage(i.binding, i); + setSymbol(i, e.resolveMethodBinding()); + usage(binding(i), i); t.complete(convertTypeArguments(e.typeArguments()), i); return t; } @@ -2426,7 +2451,7 @@ private MethodReferenceTreeImpl convertMethodReference(ExpressionMethodReference firstTokenAfter(e.getExpression(), TerminalToken.TokenNameCOLON_COLON) ); IdentifierTreeImpl i = createSimpleName(e.getName()); - usage(i.binding, i); + usage(binding(i), i); t.complete(convertTypeArguments(e.typeArguments()), i); return t; } @@ -2437,7 +2462,7 @@ private MethodReferenceTreeImpl convertMethodReference(TypeMethodReference e) { firstTokenAfter(e.getType(), TerminalToken.TokenNameCOLON_COLON) ); IdentifierTreeImpl i = createSimpleName(e.getName()); - usage(i.binding, i); + usage(binding(i), i); t.complete(convertTypeArguments(e.typeArguments()), i); return t; } @@ -2460,7 +2485,7 @@ private MethodReferenceTreeImpl convertMethodReference(SuperMethodReference e) { ); } IdentifierTreeImpl i = createSimpleName(e.getName()); - usage(i.binding, i); + usage(binding(i), i); t.complete(convertTypeArguments(e.typeArguments()), i); return t; } @@ -2507,7 +2532,7 @@ private ExpressionTree convertLiteral(NumberLiteral e) { default: throw new IllegalStateException(); } - ((LiteralTreeImpl) result).typeBinding = e.resolveTypeBinding(); + setType(((LiteralTreeImpl) result), e.resolveTypeBinding()); if (unaryMinus) { return new InternalPrefixUnaryExpression(Tree.Kind.UNARY_MINUS, createSyntaxToken(tokenIndex - 1), result); } @@ -2568,10 +2593,10 @@ private KeywordSuper unqualifiedKeywordSuper(ASTNode node) { InternalSyntaxToken token = firstTokenIn(node, TerminalToken.TokenNamesuper); do { if (node instanceof AbstractTypeDeclaration abstractTypeDeclaration) { - return new KeywordSuper(token, abstractTypeDeclaration.resolveBinding()); + return keywordSuper(token, abstractTypeDeclaration.resolveBinding()); } if (node instanceof AnonymousClassDeclaration anonymousClassDeclaration) { - return new KeywordSuper(token, anonymousClassDeclaration.resolveBinding()); + return keywordSuper(token, anonymousClassDeclaration.resolveBinding()); } node = node.getParent(); } while (true); @@ -2634,7 +2659,7 @@ private JavaTree.PrimitiveTypeTreeImpl convertPrimitiveType(PrimitiveType e) { throw new IllegalStateException(e.getPrimitiveTypeCode().toString()); } t.complete(convertAnnotations(e.annotations())); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } @@ -2659,7 +2684,7 @@ private JavaTree.UnionTypeTreeImpl convertUnionType(UnionType e) { } } JavaTree.UnionTypeTreeImpl t = new JavaTree.UnionTypeTreeImpl(alternatives); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } @@ -2678,7 +2703,7 @@ private TypeTree convertArrayType(ArrayType e) { createSyntaxToken(nextTokenIndex(tokenIndex, TerminalToken.TokenNameRBRACKET)) ); if (elementTypeBinding != null) { - ((JavaTree.ArrayTypeTreeImpl) t).typeBinding = elementTypeBinding.createArrayType(i + 1); + setType(((JavaTree.ArrayTypeTreeImpl) t), elementTypeBinding.createArrayType(i + 1)); } } return t; @@ -2700,7 +2725,7 @@ private JavaTree.ParameterizedTypeTreeImpl convertParameterizedType(Parameterize ) ) ); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } @@ -2711,7 +2736,7 @@ private MemberSelectExpressionTreeImpl convertQualifiedType(QualifiedType e) { createSimpleName(e.getName()) ); ((IdentifierTreeImpl) t.identifier()).complete(convertAnnotations(e.annotations())); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } @@ -2722,7 +2747,7 @@ private MemberSelectExpressionTreeImpl convertNamedQualifiedType(NameQualifiedTy createSimpleName(e.getName()) ); ((IdentifierTreeImpl) t.identifier()).complete(convertAnnotations(e.annotations())); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } @@ -2743,7 +2768,7 @@ private JavaTree.WildcardTreeImpl convertWildcardType(WildcardType e) { ).complete(questionToken); } t.complete(convertAnnotations(e.annotations())); - t.typeBinding = e.resolveBinding(); + setType(t, e.resolveBinding()); return t; } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSema.java b/java-frontend/src/main/java/org/sonar/java/model/JSema.java index f3c65fd7534..201cee203f9 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSema.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSema.java @@ -19,10 +19,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTUtils; @@ -41,7 +40,6 @@ public final class JSema implements Sema { private final AST ast; - final Set undefinedTypes = new HashSet<>(); final Map declarations = new HashMap<>(); final Map> usages = new HashMap<>(); private final Map types = new HashMap<>(); @@ -55,6 +53,28 @@ public final class JSema implements Sema { this.ast = ast; } + Supplier typeSupplier(@Nullable ITypeBinding binding) { + return binding == null ? SemanticValues.UNKNOWN_TYPE : new EcjSemanticAttachment<>(binding, this::type); + } + + Supplier methodSupplier(@Nullable IMethodBinding binding) { + return binding == null ? SemanticValues.UNKNOWN_METHOD : new EcjSemanticAttachment<>(binding, this::methodSymbol); + } + + Supplier symbolSupplier(@Nullable IBinding binding) { + return binding == null ? SemanticValues.UNKNOWN_SYMBOL : new EcjSemanticAttachment<>(binding, this::symbol); + } + + private Symbol symbol(IBinding binding) { + return switch (binding.getKind()) { + case IBinding.TYPE -> typeSymbol((ITypeBinding) binding); + case IBinding.METHOD -> methodSymbol((IMethodBinding) binding); + case IBinding.VARIABLE -> variableSymbol((IVariableBinding) binding); + case IBinding.PACKAGE -> packageSymbol((IPackageBinding) binding); + default -> Symbol.UNKNOWN_SYMBOL; + }; + } + public JType type(ITypeBinding typeBinding) { JType cached = types.get(typeBinding); if (cached != null) { @@ -183,11 +203,9 @@ IAnnotationBinding[] resolvePackageAnnotations(String packageName) { return ASTUtils.resolvePackageAnnotations(ast, packageName); } + @Override public Runnable getEnvironmentCleaner() { return ASTUtils.getEnvironmentCleaner(ast); } - public Set undefinedTypes() { - return Collections.unmodifiableSet(undefinedTypes); - } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java index 54365f512a0..3d0425a6c45 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSymbol.java @@ -26,7 +26,6 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.Modifier; -import org.sonar.java.model.declaration.ClassTreeImpl; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.semantic.Type; @@ -230,7 +229,7 @@ private Symbol ownerOfRecordComponentConstant(IVariableBinding variableBinding) case CLASS, RECORD, ENUM: - JTypeSymbol typeSymbol = sema.typeSymbol(((ClassTreeImpl) node).typeBinding); + JTypeSymbol typeSymbol = sema.typeSymbol(EcjTreeBindings.typeBinding(node)); if (initializerBlock) { return sema.initializerBlockSymbol(typeSymbol); } @@ -460,7 +459,7 @@ private TypeSymbol variableEnclosingClass(IVariableBinding variableBinding) { ENUM: // variable declaration in a static or instance initializer // or local variable declaration in recovered method - return sema.typeSymbol(((ClassTreeImpl) node).typeBinding); + return sema.typeSymbol(EcjTreeBindings.typeBinding(node)); default: // continue exploring parent break; diff --git a/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java b/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java index 75f3c30bf89..faf20e1af32 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JTypeSymbol.java @@ -168,7 +168,7 @@ public Set superTypes() { return superTypes; } - private void collectSuperTypes(Set result, JSema sema, ITypeBinding typeBinding) { + private static void collectSuperTypes(Set result, JSema sema, ITypeBinding typeBinding) { ITypeBinding s = typeBinding.getSuperclass(); if (s != null) { result.add(sema.type(s)); diff --git a/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java b/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java index bf41aee95c2..1a36c010640 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java @@ -21,22 +21,21 @@ import java.util.Collections; import java.util.Deque; import java.util.EnumMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; import org.sonar.java.Preconditions; import org.sonar.java.annotations.Beta; import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl; import org.sonar.java.model.declaration.AnnotationTreeImpl; import org.sonar.java.model.expression.AssessableExpressionTree; import org.sonar.java.model.expression.TypeArgumentListTreeImpl; +import org.sonar.plugins.java.api.semantic.Sema; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.ArrayTypeTree; @@ -164,7 +163,9 @@ public static class CompilationUnitTreeImpl extends JavaTree implements Compilat @Nullable private final ModuleDeclarationTree moduleDeclaration; private final SyntaxToken eofToken; - public JSema sema; + public Sema sema; + + final Set undefinedTypes = new HashSet<>(); private final Map> warnings = new EnumMap<>(JProblem.Type.class); @@ -198,6 +199,10 @@ public List warnings(JProblem.Type type) { return Collections.unmodifiableList(new ArrayList<>(warnings.getOrDefault(type, Collections.emptySet()))); } + public Set undefinedTypes() { + return Collections.unmodifiableSet(undefinedTypes); + } + @Override public void accept(TreeVisitor visitor) { visitor.visitCompilationUnit(this); @@ -326,7 +331,7 @@ public static class ImportTreeImpl extends JavaTree implements ImportTree { @Nullable private final SyntaxToken moduleToken; - public IBinding binding; + public Supplier symbolSupplier = () -> null; public ImportTreeImpl(InternalSyntaxToken importToken, @Nullable InternalSyntaxToken staticToken, @Nullable InternalSyntaxToken moduleToken, @@ -342,17 +347,7 @@ public ImportTreeImpl(InternalSyntaxToken importToken, @Nullable InternalSyntaxT @Nullable public Symbol symbol() { - if (binding != null) { - switch (binding.getKind()) { - case IBinding.TYPE: - return root.sema.typeSymbol((ITypeBinding) binding); - case IBinding.METHOD: - return root.sema.methodSymbol((IMethodBinding) binding); - case IBinding.VARIABLE: - return root.sema.variableSymbol((IVariableBinding) binding); - } - } - return null; + return symbolSupplier.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/KeywordSuper.java b/java-frontend/src/main/java/org/sonar/java/model/KeywordSuper.java index 91ec5318ce1..d3027535538 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/KeywordSuper.java +++ b/java-frontend/src/main/java/org/sonar/java/model/KeywordSuper.java @@ -16,18 +16,13 @@ */ package org.sonar.java.model; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.java.model.expression.IdentifierTreeImpl; -import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.Type; -import javax.annotation.Nullable; - final class KeywordSuper extends IdentifierTreeImpl { - KeywordSuper(InternalSyntaxToken token, @Nullable ITypeBinding typeBinding) { + KeywordSuper(InternalSyntaxToken token) { super(token); - this.typeBinding = typeBinding; } @Override @@ -35,12 +30,4 @@ public Type symbolType() { return symbol().type(); } - @Override - public Symbol symbol() { - if (typeBinding == null) { - return Symbol.UNKNOWN_SYMBOL; - } - return root.sema.typeSymbol(typeBinding).superSymbol; - } - } diff --git a/java-frontend/src/main/java/org/sonar/java/model/KeywordThis.java b/java-frontend/src/main/java/org/sonar/java/model/KeywordThis.java index 8d28995961b..43f7031baa8 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/KeywordThis.java +++ b/java-frontend/src/main/java/org/sonar/java/model/KeywordThis.java @@ -16,25 +16,12 @@ */ package org.sonar.java.model; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.java.model.expression.IdentifierTreeImpl; -import org.sonar.plugins.java.api.semantic.Symbol; - -import javax.annotation.Nullable; final class KeywordThis extends IdentifierTreeImpl { - KeywordThis(InternalSyntaxToken token, @Nullable ITypeBinding typeBinding) { + KeywordThis(InternalSyntaxToken token) { super(token); - this.typeBinding = typeBinding; - } - - @Override - public Symbol symbol() { - if (typeBinding == null) { - return Symbol.UNKNOWN_SYMBOL; - } - return root.sema.typeSymbol(typeBinding).thisSymbol; } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/SemanticValues.java b/java-frontend/src/main/java/org/sonar/java/model/SemanticValues.java new file mode 100644 index 00000000000..24947f618e8 --- /dev/null +++ b/java-frontend/src/main/java/org/sonar/java/model/SemanticValues.java @@ -0,0 +1,33 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import java.util.function.Supplier; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.Type; + +public final class SemanticValues { + + public static final Supplier UNKNOWN_TYPE = () -> Type.UNKNOWN; + public static final Supplier UNKNOWN_SYMBOL = () -> Symbol.UNKNOWN_SYMBOL; + public static final Supplier UNKNOWN_TYPE_SYMBOL = () -> Symbol.TypeSymbol.UNKNOWN_TYPE; + public static final Supplier UNKNOWN_METHOD = () -> Symbol.MethodSymbol.UNKNOWN_METHOD; + + private SemanticValues() { + } + +} diff --git a/java-frontend/src/main/java/org/sonar/java/model/TypeParameterTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/TypeParameterTreeImpl.java index d7814d9242b..7623c88e409 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/TypeParameterTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/TypeParameterTreeImpl.java @@ -19,8 +19,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl; import org.sonar.java.model.expression.IdentifierTreeImpl; import org.sonar.plugins.java.api.semantic.Symbol; @@ -39,8 +39,7 @@ public class TypeParameterTreeImpl extends JavaTree implements TypeParameterTree private final SyntaxToken extendsToken; private final QualifiedIdentifierListTreeImpl bounds; - @Nullable - ITypeBinding typeBinding; + Supplier typeSymbol = SemanticValues.UNKNOWN_SYMBOL; public TypeParameterTreeImpl(IdentifierTreeImpl identifier) { this(identifier, null, QualifiedIdentifierListTreeImpl.emptyList()); @@ -90,8 +89,6 @@ public List children() { } public Symbol symbol() { - return typeBinding != null - ? root.sema.typeSymbol(typeBinding) - : Symbol.UNKNOWN_SYMBOL; + return typeSymbol.get(); } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java index aa7ac772899..754a97fe91f 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java @@ -16,9 +16,9 @@ */ package org.sonar.java.model.declaration; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl; import org.sonar.java.ast.parser.TypeParameterListTreeImpl; +import org.sonar.java.model.SemanticValues; import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.InternalSyntaxToken; import org.sonar.java.model.JavaTree; @@ -39,6 +39,7 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; public class ClassTreeImpl extends JavaTree implements ClassTree { @@ -68,8 +69,7 @@ public class ClassTreeImpl extends JavaTree implements ClassTree { @Nullable private SyntaxToken permitsKeyword; private ListTree permittedTypes; - @Nullable - public ITypeBinding typeBinding; + public Supplier typeSymbol = SemanticValues.UNKNOWN_TYPE_SYMBOL; public ClassTreeImpl(Kind kind, @Nullable SyntaxToken openBraceToken, List members, @Nullable SyntaxToken closeBraceToken) { this.kind = kind; @@ -218,9 +218,7 @@ public SyntaxToken closeBraceToken() { @Override public Symbol.TypeSymbol symbol() { - return typeBinding != null - ? root.sema.typeSymbol(typeBinding) - : Symbol.TypeSymbol.UNKNOWN_TYPE; + return typeSymbol.get(); } @Nullable diff --git a/java-frontend/src/main/java/org/sonar/java/model/declaration/MethodTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/declaration/MethodTreeImpl.java index 188637e08ba..6faae1517da 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/declaration/MethodTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/declaration/MethodTreeImpl.java @@ -20,8 +20,8 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.IMethodBinding; import org.sonar.java.Preconditions; import org.sonar.java.ast.parser.FormalParametersListTreeImpl; import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl; @@ -31,6 +31,7 @@ import org.sonar.java.model.JUtils; import org.sonar.java.model.JavaTree; import org.sonar.java.model.ModifiersUtils; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.BlockTree; @@ -73,8 +74,7 @@ public class MethodTreeImpl extends JavaTree implements MethodTree { @Nullable private CFG cfg; - @Nullable - public IMethodBinding methodBinding; + public Supplier methodSymbol = SemanticValues.UNKNOWN_METHOD; /** * Cache for {@link #isOverriding()}. @@ -218,9 +218,7 @@ public ExpressionTree defaultValue() { @Override public Symbol.MethodSymbol symbol() { - return methodBinding != null - ? root.sema.methodSymbol(methodBinding) - : Symbol.MethodSymbol.UNKNOWN_METHOD; + return methodSymbol.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java index e46d74b113a..958c4b12357 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java @@ -18,7 +18,7 @@ import java.util.Arrays; import java.util.List; -import org.eclipse.jdt.core.dom.IVariableBinding; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.InternalSyntaxToken; @@ -38,6 +38,7 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.Objects; +import java.util.function.Supplier; public class VariableTreeImpl extends JavaTree implements VariableTree { private ModifiersTree modifiers; @@ -50,7 +51,7 @@ public class VariableTreeImpl extends JavaTree implements VariableTree { @Nullable private SyntaxToken endToken; - public IVariableBinding variableBinding; + public Supplier variableSymbol = SemanticValues.UNKNOWN_SYMBOL; public VariableTreeImpl( ModifiersTree modifiers, @@ -131,9 +132,7 @@ public SyntaxToken equalToken() { @Override public Symbol symbol() { - return variableBinding != null - ? root.sema.variableSymbol(variableBinding) - : Symbol.UNKNOWN_SYMBOL; + return variableSymbol.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/expression/IdentifierTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/expression/IdentifierTreeImpl.java index 166f6275421..7c329da2cf6 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/expression/IdentifierTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/expression/IdentifierTreeImpl.java @@ -19,14 +19,11 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.IPackageBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; +import java.util.function.Supplier; import org.sonar.java.model.InternalSyntaxToken; import org.sonar.java.model.JLabelSymbol; import org.sonar.java.model.JavaTree; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.AnnotationTree; import org.sonar.plugins.java.api.tree.IdentifierTree; @@ -41,7 +38,7 @@ public class IdentifierTreeImpl extends AssessableExpressionTree implements Iden private final boolean isUnnamedVariable; private List annotations; - public IBinding binding; + public Supplier symbolSupplier = SemanticValues.UNKNOWN_SYMBOL; public JLabelSymbol labelSymbol; public IdentifierTreeImpl(InternalSyntaxToken nameToken) { @@ -81,16 +78,10 @@ public boolean isUnnamedVariable() { @Override public Symbol symbol() { - if (binding != null && !isUnnamedVariable) { - switch (binding.getKind()) { - case IBinding.TYPE: - return root.sema.typeSymbol((ITypeBinding) binding); - case IBinding.METHOD: - return root.sema.methodSymbol((IMethodBinding) binding); - case IBinding.VARIABLE: - return root.sema.variableSymbol((IVariableBinding) binding); - case IBinding.PACKAGE: - return root.sema.packageSymbol((IPackageBinding) binding); + if (!isUnnamedVariable) { + Symbol resolved = symbolSupplier.get(); + if (resolved != Symbol.UNKNOWN_SYMBOL) { + return resolved; } } if (labelSymbol != null) { diff --git a/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java index d737562cf49..19d378eaf4a 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java @@ -17,8 +17,8 @@ package org.sonar.java.model.expression; import java.util.Arrays; -import org.eclipse.jdt.core.dom.IMethodBinding; import org.sonar.java.cfg.CFG; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.InternalSyntaxToken; @@ -33,6 +33,7 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; public class LambdaExpressionTreeImpl extends AssessableExpressionTree implements LambdaExpressionTree { @@ -45,8 +46,7 @@ public class LambdaExpressionTreeImpl extends AssessableExpressionTree implement private final Tree body; private CFG cfg; - @Nullable - public IMethodBinding methodBinding; + public Supplier methodSymbol = SemanticValues.UNKNOWN_METHOD; public LambdaExpressionTreeImpl(@Nullable InternalSyntaxToken openParenToken, List parameters, @Nullable InternalSyntaxToken closeParenToken, InternalSyntaxToken arrowToken, Tree body) { @@ -91,9 +91,7 @@ public Tree body() { @Override public Symbol.MethodSymbol symbol() { - return methodBinding != null - ? root.sema.methodSymbol(methodBinding) - : Symbol.MethodSymbol.UNKNOWN_METHOD; + return methodSymbol.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java index 6a52cb0d654..aafd8a5f797 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java @@ -18,8 +18,8 @@ import java.util.Arrays; import java.util.List; -import org.eclipse.jdt.core.dom.IMethodBinding; import org.sonar.java.ast.parser.ArgumentListTreeImpl; +import org.sonar.java.model.SemanticValues; import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.Arguments; @@ -34,6 +34,7 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.Objects; +import java.util.function.Supplier; public class MethodInvocationTreeImpl extends AssessableExpressionTree implements MethodInvocationTree { @@ -42,8 +43,7 @@ public class MethodInvocationTreeImpl extends AssessableExpressionTree implement @Nullable private TypeArguments typeArguments; - @Nullable - public IMethodBinding methodBinding; + public Supplier methodSymbolSupplier = SemanticValues.UNKNOWN_METHOD; public MethodInvocationTreeImpl(ExpressionTree methodSelect, @Nullable TypeArguments typeArguments, ArgumentListTreeImpl arguments) { this.methodSelect = Objects.requireNonNull(methodSelect); @@ -91,9 +91,7 @@ public Symbol symbol() { @Override public Symbol.MethodSymbol methodSymbol() { - return methodBinding != null - ? root.sema.methodSymbol(methodBinding) - : Symbol.MethodSymbol.UNKNOWN_METHOD; + return methodSymbolSupplier.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/pattern/AbstractPatternTree.java b/java-frontend/src/main/java/org/sonar/java/model/pattern/AbstractPatternTree.java index 183d239479f..827aa27502e 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/pattern/AbstractPatternTree.java +++ b/java-frontend/src/main/java/org/sonar/java/model/pattern/AbstractPatternTree.java @@ -17,9 +17,10 @@ package org.sonar.java.model.pattern; import java.util.Optional; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; import org.sonar.java.model.JavaTree; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.PatternTree; import org.sonar.plugins.java.api.tree.Tree; @@ -31,19 +32,16 @@ public abstract class AbstractPatternTree extends JavaTree implements PatternTre private final Tree.Kind kind; - @Nullable - public ITypeBinding typeBinding; + public Supplier type; - AbstractPatternTree(Tree.Kind kind, @Nullable ITypeBinding typeBinding) { + AbstractPatternTree(Tree.Kind kind, @Nullable Supplier type) { this.kind = kind; - this.typeBinding = typeBinding; + this.type = type != null ? type : SemanticValues.UNKNOWN_TYPE; } @Override public Type symbolType() { - return typeBinding != null - ? root.sema.type(typeBinding) - : Type.UNKNOWN; + return type.get(); } @Override diff --git a/java-frontend/src/main/java/org/sonar/java/model/pattern/DefaultPatternTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/pattern/DefaultPatternTreeImpl.java index 797b890d3f3..6ea68d3ea19 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/pattern/DefaultPatternTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/pattern/DefaultPatternTreeImpl.java @@ -18,8 +18,9 @@ import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; +import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.DefaultPatternTree; import org.sonar.plugins.java.api.tree.SyntaxToken; import org.sonar.plugins.java.api.tree.Tree; @@ -32,8 +33,8 @@ public class DefaultPatternTreeImpl extends AbstractPatternTree implements Defau private final SyntaxToken defaultToken; - public DefaultPatternTreeImpl(SyntaxToken defaultToken, @Nullable ITypeBinding typeBinding) { - super(Tree.Kind.DEFAULT_PATTERN, typeBinding); + public DefaultPatternTreeImpl(SyntaxToken defaultToken, @Nullable Supplier type) { + super(Tree.Kind.DEFAULT_PATTERN, type); this.defaultToken = defaultToken; } diff --git a/java-frontend/src/main/java/org/sonar/java/model/pattern/GuardedPatternTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/pattern/GuardedPatternTreeImpl.java index af42ddfcdbf..7fdfcc3ede8 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/pattern/GuardedPatternTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/pattern/GuardedPatternTreeImpl.java @@ -18,8 +18,9 @@ import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; +import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.ExpressionTree; import org.sonar.plugins.java.api.tree.GuardedPatternTree; import org.sonar.plugins.java.api.tree.PatternTree; @@ -36,8 +37,8 @@ public class GuardedPatternTreeImpl extends AbstractPatternTree implements Guard private final SyntaxToken whenOperator; private final ExpressionTree expression; - public GuardedPatternTreeImpl(PatternTree pattern, SyntaxToken whenOperator, ExpressionTree expression, @Nullable ITypeBinding typeBinding) { - super(Tree.Kind.GUARDED_PATTERN, typeBinding); + public GuardedPatternTreeImpl(PatternTree pattern, SyntaxToken whenOperator, ExpressionTree expression, @Nullable Supplier type) { + super(Tree.Kind.GUARDED_PATTERN, type); this.pattern = pattern; this.whenOperator = whenOperator; this.expression = expression; diff --git a/java-frontend/src/main/java/org/sonar/java/model/pattern/TypePatternTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/pattern/TypePatternTreeImpl.java index afc45f2b891..0465826379d 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/pattern/TypePatternTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/pattern/TypePatternTreeImpl.java @@ -18,8 +18,9 @@ import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.annotation.Nullable; -import org.eclipse.jdt.core.dom.ITypeBinding; +import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TreeVisitor; import org.sonar.plugins.java.api.tree.TypePatternTree; @@ -32,8 +33,8 @@ public class TypePatternTreeImpl extends AbstractPatternTree implements TypePatt private final VariableTree patternVariable; - public TypePatternTreeImpl(VariableTree patternVariable, @Nullable ITypeBinding typeBinding) { - super(Tree.Kind.TYPE_PATTERN, typeBinding); + public TypePatternTreeImpl(VariableTree patternVariable, @Nullable Supplier type) { + super(Tree.Kind.TYPE_PATTERN, type); this.patternVariable = patternVariable; } diff --git a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Sema.java b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Sema.java index 119471fc93b..a23045a7826 100644 --- a/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Sema.java +++ b/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic/Sema.java @@ -20,4 +20,12 @@ public interface Sema { Type getClassType(String fullyQualifiedName); + /** + * Returns an operation that releases owned semantic resources. Run it only after all lazy + * queries have finished. The default implementation owns no resources. + */ + default Runnable getEnvironmentCleaner() { + return () -> {}; + } + } diff --git a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java index 2574563e9bd..dbd2713a525 100644 --- a/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/SonarComponentsTest.java @@ -1184,7 +1184,7 @@ void log_only_50_undefined_types() { // artificially populated the semantic errors with 26 unknown types and 52 errors sonarComponents.collectUndefinedTypes(DEFAULT_PATH, - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); // triggers log sonarComponents.logUndefinedTypes(); @@ -1215,7 +1215,7 @@ void remove_info_and_warning_from_log_related_to_undefined_types() { loggingLogTester.setLevel(Level.ERROR); String source = generateSource(26); sonarComponents.collectUndefinedTypes(DEFAULT_PATH, - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); sonarComponents.logUndefinedTypes(); assertThat(loggingLogTester.logs(Level.WARN)).isEmpty(); @@ -1229,7 +1229,7 @@ void log_all_undefined_types_if_less_than_threshold() { // artificially populated the semantic errors with 1 unknown types and 2 errors sonarComponents.collectUndefinedTypes(DEFAULT_PATH, - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); // triggers log sonarComponents.logUndefinedTypes(); @@ -1285,9 +1285,9 @@ void log_problems_with_list_of_paths_of_files_affected() { // artificially populated the semantic errors with 1 unknown types and 2 errors sonarComponents.collectUndefinedTypes(mainFile.toString(), - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); sonarComponents.collectUndefinedTypes(testFile.toString(), - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); sonarComponents.logUndefinedTypes(); List debugMessage = loggingLogTester.logs(Level.DEBUG); @@ -1320,7 +1320,7 @@ private void logUndefinedTypesWithOneMainAndOneTest() { // artificially populated the semantic errors with 1 unknown types and 2 errors sonarComponents.collectUndefinedTypes(DEFAULT_PATH, - ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).sema.undefinedTypes()); + ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(source)).undefinedTypes()); // Call these methods to initiate Main and Test ClassPath sonarComponents.getJavaClasspath(); diff --git a/java-frontend/src/test/java/org/sonar/java/ast/JavaAstScannerTest.java b/java-frontend/src/test/java/org/sonar/java/ast/JavaAstScannerTest.java index 9c7d66bc68e..33339029e15 100644 --- a/java-frontend/src/test/java/org/sonar/java/ast/JavaAstScannerTest.java +++ b/java-frontend/src/test/java/org/sonar/java/ast/JavaAstScannerTest.java @@ -80,6 +80,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.clearSymbol; class JavaAstScannerTest { @@ -469,7 +470,7 @@ void test_modifyCompilationUnit_modify_ast() { assertThat(clazz.simpleName().name()).isEqualTo("C"); assertThat(clazz.simpleName().symbol().isUnknown()).isFalse(); IdentifierTreeImpl identifierTree = (IdentifierTreeImpl) clazz.simpleName(); - identifierTree.binding = null; + clearSymbol(identifierTree); }); } diff --git a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java index 678c8127bad..ebc0270a67c 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java @@ -25,7 +25,12 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -38,6 +43,22 @@ */ class ClassesLayoutTest { + @Test + void shared_model_and_contracts_have_no_compiler_imports() throws Exception { + Path model = Path.of("src/main/java/org/sonar/java/model"); + Set sharedFiles = Set.of("AbstractTypedTree.java", "JavaTree.java", "KeywordThis.java", "KeywordSuper.java", + "TypeParameterTreeImpl.java", "JUtils.java", "JSymbolMetadata.java", "JSymbolMetadataNullabilityHelper.java", + "JProblem.java", "JWarning.java", "SemanticValues.java"); + try (Stream files = Files.walk(model)) { + for (Path file : files.filter(path -> path.toString().endsWith(".java")).toList()) { + if (sharedFiles.contains(file.getFileName().toString()) || List.of("declaration", "expression", "pattern").contains(file.getParent().getFileName().toString())) { + assertThat(Files.readAllLines(file).stream().filter(line -> line.startsWith("import "))).as(file.toString()) + .noneMatch(line -> line.contains("org.eclipse.jdt")); + } + } + } + } + @Test void token() { assertThat(countFields(InternalSyntaxToken.class)).isEqualTo(7); diff --git a/java-frontend/src/test/java/org/sonar/java/model/EcjTestSupport.java b/java-frontend/src/test/java/org/sonar/java/model/EcjTestSupport.java new file mode 100644 index 00000000000..353ed595c70 --- /dev/null +++ b/java-frontend/src/test/java/org/sonar/java/model/EcjTestSupport.java @@ -0,0 +1,77 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.model; + +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.sonar.java.model.declaration.ClassTreeImpl; +import org.sonar.java.model.declaration.VariableTreeImpl; +import org.sonar.java.model.expression.IdentifierTreeImpl; +import org.sonar.plugins.java.api.tree.CompilationUnitTree; + +public final class EcjTestSupport { + + private EcjTestSupport() { + } + + public static JSema sema(CompilationUnitTree tree) { + return (JSema) ((JavaTree.CompilationUnitTreeImpl) tree).sema; + } + + public static ITypeBinding typeBinding(Object tree) { + return EcjTreeBindings.typeBinding(tree); + } + + public static IMethodBinding methodBinding(Object tree) { + return EcjTreeBindings.methodBinding(tree); + } + + public static IVariableBinding variableBinding(VariableTreeImpl tree) { + return EcjTreeBindings.variableBinding(tree); + } + + public static IBinding binding(Object tree) { + return EcjTreeBindings.binding(tree); + } + + public static void clearType(JavaTree tree) { + if (tree instanceof AbstractTypedTree typed) { + typed.type = SemanticValues.UNKNOWN_TYPE; + } else if (tree instanceof ClassTreeImpl cls) { + cls.typeSymbol = SemanticValues.UNKNOWN_TYPE_SYMBOL; + } else if (tree instanceof TypeParameterTreeImpl parameter) { + parameter.typeSymbol = SemanticValues.UNKNOWN_SYMBOL; + } else { + throw new IllegalArgumentException("No type attachment: " + tree.getClass()); + } + if (tree instanceof KeywordThis || tree instanceof KeywordSuper) { + ((IdentifierTreeImpl) tree).symbolSupplier = SemanticValues.UNKNOWN_SYMBOL; + } + } + + public static void clearSymbol(JavaTree tree) { + if (tree instanceof IdentifierTreeImpl identifier) { + identifier.symbolSupplier = SemanticValues.UNKNOWN_SYMBOL; + } else if (tree instanceof VariableTreeImpl variable) { + variable.variableSymbol = SemanticValues.UNKNOWN_SYMBOL; + } else { + throw new IllegalArgumentException("No symbol attachment: " + tree.getClass()); + } + } +} diff --git a/java-frontend/src/test/java/org/sonar/java/model/JMethodSymbolTest.java b/java-frontend/src/test/java/org/sonar/java/model/JMethodSymbolTest.java index 24fa6f9dd28..6f0595798fd 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JMethodSymbolTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JMethodSymbolTest.java @@ -52,6 +52,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.methodBinding; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; import static org.sonar.java.model.JUtilsTest.firstClass; import static org.sonar.java.model.JUtilsTest.firstMethod; import static org.sonar.java.model.JUtilsTest.nthMethod; @@ -63,15 +66,15 @@ void test() { JavaTree.CompilationUnitTreeImpl cu = test("class C { C m(C p) throws Exception { return null; } }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.parameterTypes()) - .containsOnly(cu.sema.type(Objects.requireNonNull(c.typeBinding))); + .containsOnly(sema(cu).type(Objects.requireNonNull(typeBinding(c)))); assertThat(symbol.declarationParameters()) .containsExactly(m.parameters().get(0).symbol()); assertThat(symbol.returnType()) - .isSameAs(cu.sema.typeSymbol(Objects.requireNonNull(c.typeBinding))); + .isSameAs(sema(cu).typeSymbol(Objects.requireNonNull(typeBinding(c)))); assertThat(symbol.thrownTypes()) - .containsOnly(cu.sema.type(Objects.requireNonNull(cu.sema.resolveType("java.lang.Exception")))); + .containsOnly(sema(cu).type(Objects.requireNonNull(sema(cu).resolveType("java.lang.Exception")))); } @Test @@ -80,7 +83,7 @@ void test_with_generics() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); Symbol.MethodSymbol declarationSymbol = m.symbol(); - JType declarationParameterType = cu.sema.type(Objects.requireNonNull((m.methodBinding.getParameterTypes()[0]))); + JType declarationParameterType = sema(cu).type(Objects.requireNonNull((methodBinding(m).getParameterTypes()[0]))); assertThat(declarationSymbol.parameterTypes()) .containsOnly(declarationParameterType); Symbol declarationParameterSymbol = m.parameters().get(0).symbol(); @@ -193,7 +196,7 @@ void testSingleInheritance() { + "class Clazz implements A { public void a() {} }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(1); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).containsOnly(retrieveMethodSymbol("A", "a", cu)); } @@ -208,7 +211,7 @@ class C extends B { public void a() {} } """); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(3); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).containsExactly( retrieveMethodSymbol("B", "a", cu), @@ -221,9 +224,9 @@ void testObjectExtensionFromClass() { JavaTree.CompilationUnitTreeImpl cu = test("class Clazz { public boolean equals(Object other) { return false; } }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); - assertThat(symbol.overriddenSymbols()).containsExactly(objectEqualsMethod(cu.sema)); + assertThat(symbol.overriddenSymbols()).containsExactly(objectEqualsMethod(sema(cu))); } @Test @@ -231,9 +234,9 @@ void testObjectExtensionFromInterface() { JavaTree.CompilationUnitTreeImpl cu = test("interface Interface { boolean equals(Object other) { return false; } }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); - assertThat(symbol.overriddenSymbols()).containsExactly(objectEqualsMethod(cu.sema)); + assertThat(symbol.overriddenSymbols()).containsExactly(objectEqualsMethod(sema(cu))); } private static JMethodSymbol objectEqualsMethod(JSema sema) { @@ -253,7 +256,7 @@ class C implements A, B { public void a() { } } """); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(2); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).containsExactly( retrieveMethodSymbol("A", "a", cu), @@ -267,7 +270,7 @@ void testUnknownInheritance() { + "class C implements KnownInterface, UnknownInterface { public void a() { } }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(1); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).containsExactly( retrieveMethodSymbol("KnownInterface", "a", cu)); @@ -279,7 +282,7 @@ void testUnknownExtends() { + "class B extends Unknown { public void a() { } }"); ClassTreeImpl a = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) a.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).isEmpty(); } @@ -291,7 +294,7 @@ void testBindingReturnNullSuperClass() { MethodTreeImpl m = (MethodTreeImpl) a.members().get(0); // We create a real JMethodSymbol because it is not possible to mock JMethodSymbol (it is final), and not easy // to mock the arguments either, because the constructor is using an external helper method. - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); ITypeBinding iTypeBinding = mock(ITypeBinding.class); // In some rare situation, the "getSuperclass" can return null. It is not easy to write code reproducing the issue, we test it by "hand". @@ -315,10 +318,10 @@ class Clazz implements A, C { public boolean equals(Object other) { return false """); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(3); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - JMethodSymbol symbol = cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)); + JMethodSymbol symbol = sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))); assertThat(symbol.overriddenSymbols()).containsExactly( - objectEqualsMethod(cu.sema), + objectEqualsMethod(sema(cu)), retrieveMethodSymbol("A", "equals", cu), retrieveMethodSymbol("B", "equals", cu)); } @@ -349,13 +352,13 @@ void signature() { MethodTreeImpl method = (MethodTreeImpl) c.members().get(1); ReturnStatementTreeImpl s = (ReturnStatementTreeImpl) Objects.requireNonNull(method.block()).body().get(0); MethodInvocationTreeImpl methodInvocation = Objects.requireNonNull((MethodInvocationTreeImpl) s.expression()); - assertThat(cu.sema.methodSymbol(Objects.requireNonNull(constructor.methodBinding)).signature()) + assertThat(sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(constructor))).signature()) .isEqualTo(constructor.symbol().signature()) .isEqualTo("org.example.C#()V"); - assertThat(cu.sema.methodSymbol(Objects.requireNonNull(method.methodBinding)).signature()) + assertThat(sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(method))).signature()) .isEqualTo(method.symbol().signature()) .isEqualTo("org.example.C#m(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - assertThat(cu.sema.methodSymbol(Objects.requireNonNull(methodInvocation.methodBinding)).signature()) + assertThat(sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(methodInvocation))).signature()) .isEqualTo(method.symbol().signature()) .isEqualTo("org.example.C#m(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); } @@ -424,9 +427,9 @@ void support_unexpected_IMethodBinding_null_return_type() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); // simulate broken semantic with getReturnType() == null - IMethodBinding brokenMethodBinding = spy(m.methodBinding); + IMethodBinding brokenMethodBinding = spy(methodBinding(m)); when(brokenMethodBinding.getReturnType()).thenReturn(null); - JMethodSymbol methodSymbol = new JMethodSymbol(cu.sema, brokenMethodBinding); + JMethodSymbol methodSymbol = new JMethodSymbol(sema(cu), brokenMethodBinding); assertThat(methodSymbol.returnType().isUnknown()).isTrue(); assertThat(methodSymbol.isNativeMethod()).isFalse(); assertThat(methodSymbol.metadata()).isSameAs(Symbols.EMPTY_METADATA); @@ -460,12 +463,12 @@ private static JMethodSymbol getJMethodSymbolFromClassText(String classText, boo ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); if(brokenSemantic){ - IMethodBinding brokenMethodBinding = spy(m.methodBinding); + IMethodBinding brokenMethodBinding = spy(methodBinding(m)); when(brokenMethodBinding.getReturnType()).thenReturn(null); when(brokenMethodBinding.isRecovered()).thenReturn(true); - return new JMethodSymbol(cu.sema, brokenMethodBinding); + return new JMethodSymbol(sema(cu), brokenMethodBinding); } - return new JMethodSymbol(cu.sema, m.methodBinding); + return new JMethodSymbol(sema(cu), methodBinding(m)); } private static CompilationUnitTreeImpl test(String source) { diff --git a/java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java b/java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java index 5190459774c..1c9e79b9aa1 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java @@ -48,6 +48,7 @@ import org.sonar.java.model.expression.MethodReferenceTreeImpl; import org.sonar.java.model.expression.NewClassTreeImpl; import org.sonar.java.model.location.InternalRange; +import org.sonar.java.model.pattern.TypePatternTreeImpl; import org.sonar.java.model.statement.BlockTreeImpl; import org.sonar.java.model.statement.BreakStatementTreeImpl; import org.sonar.java.model.statement.ContinueStatementTreeImpl; @@ -97,11 +98,37 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.sonar.java.model.EcjTestSupport.binding; +import static org.sonar.java.model.EcjTestSupport.clearSymbol; +import static org.sonar.java.model.EcjTestSupport.clearType; +import static org.sonar.java.model.EcjTestSupport.methodBinding; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; +import static org.sonar.java.model.EcjTestSupport.variableBinding; import static org.sonar.java.model.assertions.TreeAssert.assertThat; import static org.sonar.java.model.assertions.TypeAssert.assertThat; class JParserSemanticTest { + @Test + void trees_accept_lazy_semantics_without_a_compiler_or_root() { + Type type = mock(Type.class); + Symbol symbol = mock(Symbol.class); + IdentifierTreeImpl identifier = new IdentifierTreeImpl(new InternalSyntaxToken(1, 0, "A", List.of(), false)); + assertThat(identifier.symbolType()).isSameAs(Type.UNKNOWN); + assertThat(identifier.symbol()).isSameAs(Symbol.UNKNOWN_SYMBOL); + identifier.type = () -> type; + identifier.symbolSupplier = () -> symbol; + assertThat(identifier.symbolType()).isSameAs(type); + assertThat(identifier.symbol()).isSameAs(symbol); + ClassTreeImpl declaration = new ClassTreeImpl(Tree.Kind.CLASS, null, List.of(), null); + Symbol.TypeSymbol typeSymbol = mock(Symbol.TypeSymbol.class); + declaration.typeSymbol = () -> typeSymbol; + assertThat(declaration.symbol()).isSameAs(typeSymbol); + assertThat(new TypePatternTreeImpl(null, () -> type).symbolType()).isSameAs(type); + } + @Test void should_not_throw_RecognitionException_in_case_of_non_syntax_errors() { test("import nonexisting;"); @@ -110,17 +137,17 @@ void should_not_throw_RecognitionException_in_case_of_non_syntax_errors() { @Test void expression_null_literal() { AbstractTypedTree e = (AbstractTypedTree) expression("null"); - assertThat(e.typeBinding).isNotNull(); + assertThat(typeBinding(e)).isNotNull(); } @Test void expression_literal() { InternalPrefixUnaryExpression e = (InternalPrefixUnaryExpression) expression("-2147483648"); // Integer.MIN_VALUE - assertThat(e.typeBinding).isNotNull(); + assertThat(typeBinding(e)).isNotNull(); AbstractTypedTree t = (AbstractTypedTree) e.expression(); - assertThat(t.typeBinding) + assertThat(typeBinding(t)) .isNotNull() - .isSameAs(e.typeBinding); + .isSameAs(typeBinding(e)); } /** @@ -130,8 +157,8 @@ void expression_literal() { void extended_operands() { BinaryExpressionTreeImpl e = (BinaryExpressionTreeImpl) expression("1 - 2 - 3"); BinaryExpressionTreeImpl leftOperand = (BinaryExpressionTreeImpl) e.leftOperand(); - assertThat(e.typeBinding).isNotNull(); - assertThat(leftOperand.typeBinding).isSameAs(e.typeBinding); + assertThat(typeBinding(e)).isNotNull(); + assertThat(typeBinding(leftOperand)).isSameAs(typeBinding(e)); } /** @@ -144,8 +171,8 @@ void expression_simple_name() { MethodTreeImpl m = (MethodTreeImpl) c.members().get(1); ReturnStatementTree s = (ReturnStatementTree) m.block().body().get(0); IdentifierTreeImpl i = (IdentifierTreeImpl) s.expression(); - assertThat(i.binding).isNotNull(); - assertThat(cu.sema.usages.get(i.binding)).containsOnly(i); + assertThat(binding(i)).isNotNull(); + assertThat(sema(cu).usages.get(binding(i))).containsOnly(i); } /** @@ -161,32 +188,32 @@ void expression_qualified_name() { MemberSelectExpressionTreeImpl javaLangSystemOut = (MemberSelectExpressionTreeImpl) s.expression(); MemberSelectExpressionTreeImpl javaLangSystem = (MemberSelectExpressionTreeImpl) javaLangSystemOut.expression(); - assertThat(javaLangSystem.typeBinding).isNotNull(); - assertThat(cu.sema.type(javaLangSystem.typeBinding).is("java.lang.System")) + assertThat(typeBinding(javaLangSystem)).isNotNull(); + assertThat(sema(cu).type(typeBinding(javaLangSystem)).is("java.lang.System")) .isEqualTo(javaLangSystem.symbolType().is("java.lang.System")) .isTrue(); MemberSelectExpressionTreeImpl javaLang = (MemberSelectExpressionTreeImpl) javaLangSystem.expression(); - assertThat(javaLang.typeBinding).isNull(); + assertThat(typeBinding(javaLang)).isNull(); IdentifierTreeImpl java = (IdentifierTreeImpl) javaLang.expression(); - assertThat(java.typeBinding).isNull(); - assertThat(java.binding).isNotNull(); - assertThat(java.binding.getKind()).isSameAs(IBinding.PACKAGE); + assertThat(typeBinding(java)).isNull(); + assertThat(binding(java)).isNotNull(); + assertThat(binding(java).getKind()).isSameAs(IBinding.PACKAGE); assertThat(java.symbol().isPackageSymbol()).isTrue(); IdentifierTreeImpl lang = (IdentifierTreeImpl) javaLang.identifier(); - assertThat(lang.typeBinding).isNull(); - assertThat(lang.binding).isNotNull(); - assertThat(lang.binding.getKind()).isSameAs(IBinding.PACKAGE); + assertThat(typeBinding(lang)).isNull(); + assertThat(binding(lang)).isNotNull(); + assertThat(binding(lang).getKind()).isSameAs(IBinding.PACKAGE); assertThat(lang.symbol().isPackageSymbol()).isTrue(); IdentifierTreeImpl system = (IdentifierTreeImpl) javaLangSystem.identifier(); - assertThat(system.binding) + assertThat(binding(system)) .isNotNull() - .isEqualTo(system.typeBinding); - assertThat(system.binding.getKind()).isSameAs(IBinding.TYPE); - assertThat(cu.sema.type(system.typeBinding).is("java.lang.System")) + .isEqualTo(typeBinding(system)); + assertThat(binding(system).getKind()).isSameAs(IBinding.TYPE); + assertThat(sema(cu).type(typeBinding(system)).is("java.lang.System")) .isEqualTo(system.symbolType().is("java.lang.System")) .isTrue(); } @@ -201,12 +228,12 @@ void expression_this() { MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); KeywordThis keywordThis = Objects.requireNonNull((KeywordThis) s.expression()); - assertThat(keywordThis.typeBinding) + assertThat(typeBinding(keywordThis)) .isNotNull() - .isSameAs(Objects.requireNonNull((ClassTreeImpl) keywordThis.symbolType().symbol().declaration()).typeBinding) - .isSameAs(c.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) keywordThis.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(c)); assertThat(keywordThis.symbol()) - .isSameAs(cu.sema.typeSymbol(c.typeBinding).thisSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c)).thisSymbol); assertThat(keywordThis.symbol().isVariableSymbol()) .isTrue(); } @@ -222,17 +249,17 @@ void expression_this_qualified() { MethodTreeImpl m = (MethodTreeImpl) c2.members().get(0); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MemberSelectExpressionTreeImpl e = Objects.requireNonNull((MemberSelectExpressionTreeImpl) s.expression()); - assertThat(e.typeBinding) + assertThat(typeBinding(e)) .isNotNull() - .isSameAs(Objects.requireNonNull((ClassTreeImpl) e.symbolType().symbol().declaration()).typeBinding) - .isSameAs(c1.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) e.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(c1)); KeywordThis keywordThis = (KeywordThis) e.identifier(); - assertThat(keywordThis.typeBinding) + assertThat(typeBinding(keywordThis)) .isNotNull() - .isSameAs(Objects.requireNonNull((ClassTreeImpl) keywordThis.symbolType().symbol().declaration()).typeBinding) - .isSameAs(c1.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) keywordThis.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(c1)); assertThat(keywordThis.symbol()) - .isSameAs(cu.sema.typeSymbol(c1.typeBinding).thisSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c1)).thisSymbol); assertThat(keywordThis.symbol().isVariableSymbol()) .isTrue(); } @@ -248,16 +275,16 @@ void expression_field_access() { MethodTreeImpl m = (MethodTreeImpl) c.members().get(1); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MemberSelectExpressionTreeImpl expression = Objects.requireNonNull((MemberSelectExpressionTreeImpl) s.expression()); - assertThat(expression.typeBinding) + assertThat(typeBinding(expression)) .isNotNull(); IdentifierTreeImpl identifier = (IdentifierTreeImpl) expression.identifier(); - assertThat(identifier.typeBinding) - .isSameAs(expression.typeBinding); - assertThat(identifier.binding) + assertThat(typeBinding(identifier)) + .isSameAs(typeBinding(expression)); + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()).variableBinding) - .isSameAs(field.variableBinding); - assertThat(cu.sema.usages.get(identifier.binding)) + .isSameAs(variableBinding(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()))) + .isSameAs(variableBinding(field)); + assertThat(sema(cu).usages.get(binding(identifier))) .containsExactlyElementsOf(identifier.symbol().usages()) .containsOnly(identifier); } @@ -279,14 +306,14 @@ void expression_super_field_access() { assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(superClass.symbol().declaration()); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(c.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c)).superSymbol); IdentifierTreeImpl identifier = (IdentifierTreeImpl) expression.identifier(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()).variableBinding) - .isSameAs(superField.variableBinding); - assertThat(cu.sema.usages.get(identifier.binding)) + .isSameAs(variableBinding(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()))) + .isSameAs(variableBinding(superField)); + assertThat(sema(cu).usages.get(binding(identifier))) .containsExactlyElementsOf(identifier.symbol().usages()) .containsOnly(identifier); } @@ -306,23 +333,23 @@ void expression_super_field_access_qualified() { MemberSelectExpressionTreeImpl expression = Objects.requireNonNull((MemberSelectExpressionTreeImpl) s.expression()); MemberSelectExpressionTreeImpl qualifiedSuper = (MemberSelectExpressionTreeImpl) expression.expression(); - assertThat(qualifiedSuper.typeBinding) - .isSameAs(Objects.requireNonNull((ClassTreeImpl) qualifiedSuper.symbolType().symbol().declaration()).typeBinding) - .isSameAs(superClass.typeBinding); + assertThat(typeBinding(qualifiedSuper)) + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) qualifiedSuper.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(superClass)); KeywordSuper keywordSuper = (KeywordSuper) qualifiedSuper.identifier(); assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(Objects.requireNonNull((ClassTreeImpl) keywordSuper.symbolType().symbol().declaration())) .isSameAs(superClass); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(t.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(t)).superSymbol); IdentifierTreeImpl identifier = (IdentifierTreeImpl) expression.identifier(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()).variableBinding) - .isSameAs(superField.variableBinding); - assertThat(cu.sema.usages.get(identifier.binding)) + .isSameAs(variableBinding(Objects.requireNonNull((VariableTreeImpl) identifier.symbol().declaration()))) + .isSameAs(variableBinding(superField)); + assertThat(sema(cu).usages.get(binding(identifier))) .containsExactlyElementsOf(identifier.symbol().usages()) .containsOnly(identifier); } @@ -337,14 +364,14 @@ void expression_type_literal() { MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MemberSelectExpressionTreeImpl e = Objects.requireNonNull((MemberSelectExpressionTreeImpl) s.expression()); - assertThat(e.typeBinding) + assertThat(typeBinding(e)) .isNotNull(); IdentifierTreeImpl i = (IdentifierTreeImpl) e.identifier(); - assertThat(i.typeBinding) + assertThat(typeBinding(i)) .isNull(); assertThat(i.symbolType().isUnknown()) .isTrue(); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); assertThat(i.symbol().isUnknown()) .isTrue(); @@ -361,16 +388,16 @@ void expression_class_instance_creation() { MethodTreeImpl constructor = (MethodTreeImpl) c.members().get(1); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); NewClassTreeImpl e = Objects.requireNonNull((NewClassTreeImpl) s.expression()); - assertThat(e.typeBinding) + assertThat(typeBinding(e)) .isNotNull() - .isSameAs(Objects.requireNonNull((ClassTreeImpl) e.symbolType().symbol().declaration()).typeBinding) - .isSameAs(c.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) e.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(c)); IdentifierTreeImpl i = (IdentifierTreeImpl) e.getConstructorIdentifier(); - assertThat(i.binding) + assertThat(binding(i)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(constructor.methodBinding); - assertThat(cu.sema.usages.get(constructor.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(constructor)); + assertThat(sema(cu).usages.get(methodBinding(constructor))) .containsExactlyElementsOf(constructor.symbol().usages()) .containsOnly(i); } @@ -387,17 +414,17 @@ void expression_anonymous_class_instance_creation() { ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); NewClassTreeImpl e = (NewClassTreeImpl) s.expression(); ClassTreeImpl b = (ClassTreeImpl) e.classBody(); - assertThat(b.typeBinding).isNotNull(); - assertThat(cu.sema.declarations.get(b.typeBinding)) + assertThat(typeBinding(b)).isNotNull(); + assertThat(sema(cu).declarations.get(typeBinding(b))) .isSameAs(b.symbol().declaration()) .isSameAs(b); IdentifierTreeImpl identifier = (IdentifierTreeImpl) e.getConstructorIdentifier(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()).methodBinding) - .isSameAs(constructor.methodBinding); - assertThat(cu.sema.usages.get(constructor.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()))) + .isSameAs(methodBinding(constructor)); + assertThat(sema(cu).usages.get(methodBinding(constructor))) .containsExactlyElementsOf(constructor.symbol().usages()) .containsOnly(identifier); } @@ -414,11 +441,11 @@ void expression_creation_reference() { ReturnStatementTreeImpl s = (ReturnStatementTreeImpl) m.block().body().get(0); MethodReferenceTreeImpl creationReference = (MethodReferenceTreeImpl) s.expression(); IdentifierTreeImpl keywordNew = (IdentifierTreeImpl) creationReference.method(); - assertThat(keywordNew.binding) + assertThat(binding(keywordNew)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()).methodBinding) - .isSameAs(constructor.methodBinding); - assertThat(cu.sema.usages.get(constructor.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()))) + .isSameAs(methodBinding(constructor)); + assertThat(sema(cu).usages.get(methodBinding(constructor))) .containsExactlyElementsOf(constructor.symbol().usages()) .containsOnly(keywordNew); } @@ -434,11 +461,11 @@ void expression_method_reference() { ReturnStatementTreeImpl s = (ReturnStatementTreeImpl) method.block().body().get(0); MethodReferenceTreeImpl creationReference = (MethodReferenceTreeImpl) s.expression(); IdentifierTreeImpl identifier = (IdentifierTreeImpl) creationReference.method(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()).methodBinding) - .isSameAs(method.methodBinding); - assertThat(cu.sema.usages.get(method.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()))) + .isSameAs(methodBinding(method)); + assertThat(sema(cu).usages.get(methodBinding(method))) .containsExactlyElementsOf(method.symbol().usages()) .containsOnly(identifier); } @@ -454,11 +481,11 @@ void expression_type_method_reference() { ReturnStatementTreeImpl s = (ReturnStatementTreeImpl) method.block().body().get(0); MethodReferenceTreeImpl creationReference = (MethodReferenceTreeImpl) s.expression(); IdentifierTreeImpl identifier = (IdentifierTreeImpl) creationReference.method(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()).methodBinding) - .isSameAs(method.methodBinding); - assertThat(cu.sema.usages.get(method.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()))) + .isSameAs(methodBinding(method)); + assertThat(sema(cu).usages.get(methodBinding(method))) .containsExactlyElementsOf(method.symbol().usages()) .containsOnly(identifier); } @@ -487,14 +514,14 @@ class S { assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(superClass.symbol().declaration()); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(c.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c)).superSymbol); IdentifierTreeImpl identifier = (IdentifierTreeImpl) creationReference.method(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()).methodBinding) - .isSameAs(superClassMethod.methodBinding); - assertThat(cu.sema.usages.get(superClassMethod.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()))) + .isSameAs(methodBinding(superClassMethod)); + assertThat(sema(cu).usages.get(methodBinding(superClassMethod))) .containsExactlyElementsOf(superClassMethod.symbol().usages()) .containsOnly(identifier); } @@ -525,14 +552,14 @@ class S { assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(superClass.symbol().declaration()); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(c.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c)).superSymbol); IdentifierTreeImpl identifier = (IdentifierTreeImpl) creationReference.method(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()).methodBinding) - .isSameAs(superClassMethod.methodBinding); - assertThat(cu.sema.usages.get(superClassMethod.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) creationReference.method().symbol().declaration()))) + .isSameAs(methodBinding(superClassMethod)); + assertThat(sema(cu).usages.get(methodBinding(superClassMethod))) .containsExactlyElementsOf(superClassMethod.symbol().usages()) .containsOnly(identifier); } @@ -614,7 +641,7 @@ void expression_pattern_instanceof() { .is("java.lang.String") .is(i.symbolType()); - assertThat(i.binding).isNotNull(); + assertThat(binding(i)).isNotNull(); assertThat(i.symbol().declaration()).isEqualTo(v); } @@ -629,15 +656,15 @@ void expression_method_invocation() { ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(method.block()).body().get(0); MethodInvocationTreeImpl methodInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(methodInvocation.methodBinding) + assertThat(methodBinding(methodInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) methodInvocation.methodSymbol().declaration()).methodBinding) - .isSameAs(method.methodBinding); + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) methodInvocation.methodSymbol().declaration()))) + .isSameAs(methodBinding(method)); IdentifierTreeImpl i = (IdentifierTreeImpl) methodInvocation.methodSelect(); - assertThat(i.binding) - .isSameAs(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(methodInvocation.methodBinding); - assertThat(cu.sema.usages.get(i.binding)) + assertThat(binding(i)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(methodInvocation)); + assertThat(sema(cu).usages.get(binding(i))) .containsExactlyElementsOf(methodInvocation.methodSymbol().usages()) .containsOnly(i); } @@ -652,12 +679,12 @@ void expression_method_invocation_recovery() { MethodTreeImpl method = (MethodTreeImpl) c.members().get(0); ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(method.block()).body().get(0); MethodInvocationTreeImpl methodInvocation = Objects.requireNonNull((MethodInvocationTreeImpl) s.expression()); - assertThat(methodInvocation.methodBinding) + assertThat(methodBinding(methodInvocation)) .isNull(); IdentifierTreeImpl i = (IdentifierTreeImpl) methodInvocation.methodSelect(); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); - assertThat(cu.sema.usages.get(Objects.requireNonNull(method.methodBinding))) + assertThat(sema(cu).usages.get(Objects.requireNonNull(methodBinding(method)))) .isNull(); } @@ -674,23 +701,23 @@ void expression_super_method_invocation() { ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MethodInvocationTreeImpl superMethodInvocation = (MethodInvocationTreeImpl) Objects.requireNonNull(s.expression()); - assertThat(superMethodInvocation.methodBinding) + assertThat(methodBinding(superMethodInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) superMethodInvocation.methodSymbol().declaration()).methodBinding) - .isSameAs(superClassMethod.methodBinding); + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) superMethodInvocation.methodSymbol().declaration()))) + .isSameAs(methodBinding(superClassMethod)); MemberSelectExpressionTreeImpl e2 = (MemberSelectExpressionTreeImpl) superMethodInvocation.methodSelect(); KeywordSuper keywordSuper = (KeywordSuper) e2.expression(); assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(superClass.symbol().declaration()); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(c.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(c)).superSymbol); IdentifierTreeImpl i = (IdentifierTreeImpl) e2.identifier(); - assertThat(i.binding) - .isSameAs(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(superMethodInvocation.methodBinding); - assertThat(cu.sema.usages.get(i.binding)) + assertThat(binding(i)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(superMethodInvocation)); + assertThat(sema(cu).usages.get(binding(i))) .containsExactlyElementsOf(superMethodInvocation.methodSymbol().usages()) .containsOnly(i); } @@ -708,13 +735,13 @@ void expression_super_method_invocation_recovery() { ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MethodInvocationTreeImpl superMethodInvocation = (MethodInvocationTreeImpl) Objects.requireNonNull(s.expression()); - assertThat(superMethodInvocation.methodBinding) + assertThat(methodBinding(superMethodInvocation)) .isNull(); MemberSelectExpressionTreeImpl e = (MemberSelectExpressionTreeImpl) superMethodInvocation.methodSelect(); IdentifierTreeImpl i = (IdentifierTreeImpl) e.identifier(); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); - assertThat(cu.sema.usages.get(Objects.requireNonNull(superClassMethod.methodBinding))) + assertThat(sema(cu).usages.get(Objects.requireNonNull(methodBinding(superClassMethod)))) .isNull(); } @@ -732,34 +759,34 @@ void expression_super_method_invocation_qualified() { ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0); MethodInvocationTreeImpl superMethodInvocation = (MethodInvocationTreeImpl) Objects.requireNonNull(s.expression()); - assertThat(superMethodInvocation.methodBinding) + assertThat(methodBinding(superMethodInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) superMethodInvocation.methodSymbol().declaration()).methodBinding) - .isSameAs(superClassMethod.methodBinding); + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) superMethodInvocation.methodSymbol().declaration()))) + .isSameAs(methodBinding(superClassMethod)); MemberSelectExpressionTreeImpl qualifiedMethodName = (MemberSelectExpressionTreeImpl) superMethodInvocation.methodSelect(); assertThat(qualifiedMethodName.symbolType().isUnknown()) .isTrue(); - assertThat(qualifiedMethodName.typeBinding) + assertThat(typeBinding(qualifiedMethodName)) .isNull(); MemberSelectExpressionTreeImpl qualifiedSuper = (MemberSelectExpressionTreeImpl) qualifiedMethodName.expression(); - assertThat(qualifiedSuper.typeBinding) + assertThat(typeBinding(qualifiedSuper)) .isNotNull() - .isSameAs(Objects.requireNonNull((ClassTreeImpl) qualifiedSuper.symbolType().symbol().declaration()).typeBinding) - .isSameAs(superClass.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull((ClassTreeImpl) qualifiedSuper.symbolType().symbol().declaration()))) + .isSameAs(typeBinding(superClass)); KeywordSuper keywordSuper = (KeywordSuper) qualifiedSuper.identifier(); assertThat(keywordSuper.symbolType().symbol().declaration()) .isSameAs(superClass); assertThat(keywordSuper.symbol()) - .isSameAs(cu.sema.typeSymbol(t.typeBinding).superSymbol); + .isSameAs(sema(cu).typeSymbol(typeBinding(t)).superSymbol); IdentifierTreeImpl identifier = (IdentifierTreeImpl) qualifiedMethodName.identifier(); - assertThat(identifier.binding) - .isSameAs(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()).methodBinding) - .isSameAs(superMethodInvocation.methodBinding); - assertThat(cu.sema.usages.get(identifier.binding)) + assertThat(binding(identifier)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()))) + .isSameAs(methodBinding(superMethodInvocation)); + assertThat(sema(cu).usages.get(binding(identifier))) .containsExactlyElementsOf(superMethodInvocation.methodSymbol().usages()) .containsOnly(identifier); } @@ -783,7 +810,7 @@ default void forEach(java.util.function.Consumer action) { MemberSelectExpressionTreeImpl iterableSuper = (MemberSelectExpressionTreeImpl) iterableSuperForeach.expression(); IdentifierTreeImpl superKeyword = (IdentifierTreeImpl) iterableSuper.identifier(); - assertThat(superKeyword.typeBinding).isNotNull(); + assertThat(typeBinding(superKeyword)).isNotNull(); assertThat(superKeyword.symbolType().fullyQualifiedName()).isEqualTo("java.lang.Iterable"); } @@ -804,7 +831,7 @@ class A { var lambda = (LambdaExpressionTree) f.initializer(); var p = (VariableTreeImpl) lambda.parameters().get(0); - Symbol newSymbol = cu.sema.variableSymbol(p.variableBinding); + Symbol newSymbol = sema(cu).variableSymbol(variableBinding(p)); assertThat(newSymbol.declaration().firstToken().range().start().line()).isEqualTo(3); Symbol newOwner = newSymbol.owner(); assertTrue(newOwner instanceof JMethodSymbol methodSymbol && methodSymbol.isLambda()); @@ -835,7 +862,7 @@ void foo() { VariableTreeImpl p = (VariableTreeImpl) lambda.parameters().get(0); // owner of lambda parameter is the field or variable to which it is assigned - Symbol newSymbol = cu.sema.variableSymbol(p.variableBinding); + Symbol newSymbol = sema(cu).variableSymbol(variableBinding(p)); Symbol newOwner = newSymbol.owner(); assertTrue(newOwner instanceof JMethodSymbol methodSymbol && methodSymbol.isLambda()); } @@ -864,8 +891,8 @@ void m() { ReturnStatementTreeImpl r = (ReturnStatementTreeImpl) body.body().get(0); LambdaExpressionTreeImpl l2 = (LambdaExpressionTreeImpl) r.expression(); - assertThat(l2.typeBinding).isNotNull(); - assertThat(cu.sema.type(l2.typeBinding).is("java.util.function.Supplier")).isTrue(); + assertThat(typeBinding(l2)).isNotNull(); + assertThat(sema(cu).type(typeBinding(l2)).is("java.util.function.Supplier")).isTrue(); } /** @@ -890,16 +917,16 @@ void foo(Class[] arr1, Class[] arr2, boolean IdentifierTreeImpl c1 = (IdentifierTreeImpl) cond.trueExpression(); IdentifierTreeImpl c2 = (IdentifierTreeImpl) cond.falseExpression(); - assertThat(cond.typeBinding).isNotNull(); - JType tcond = cu.sema.type(cond.typeBinding); + assertThat(typeBinding(cond)).isNotNull(); + JType tcond = sema(cu).type(typeBinding(cond)); assertThat(tcond.is("java.lang.Class[]")).isTrue(); - assertThat(c1.typeBinding).isNotNull(); - JType tc1 = cu.sema.type(c1.typeBinding); + assertThat(typeBinding(c1)).isNotNull(); + JType tc1 = sema(cu).type(typeBinding(c1)); assertThat(tc1.is("java.lang.Class[]")).isTrue(); - assertThat(c2.typeBinding).isNotNull(); - JType tc2 = cu.sema.type(c2.typeBinding); + assertThat(typeBinding(c2)).isNotNull(); + JType tc2 = sema(cu).type(typeBinding(c2)); assertThat(tc2.is("java.lang.Class[]")).isTrue(); assertThat(tc2) @@ -919,19 +946,19 @@ void statement_constructor_invocation() { ExpressionStatementTree s = (ExpressionStatementTree) m.block().body().get(0); MethodInvocationTreeImpl constructorInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(constructorInvocation.methodBinding) + assertThat(methodBinding(constructorInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) constructorInvocation.methodSymbol().declaration()).methodBinding) - .isSameAs(constructor2.methodBinding); - assertThat(constructorInvocation.typeBinding) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) constructorInvocation.methodSymbol().declaration()))) + .isSameAs(methodBinding(constructor2)); + assertThat(typeBinding(constructorInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull(((ClassTreeImpl) constructorInvocation.symbolType().symbol().declaration())).typeBinding) - .isSameAs(c.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull(((ClassTreeImpl) constructorInvocation.symbolType().symbol().declaration())))) + .isSameAs(typeBinding(c)); IdentifierTreeImpl i = (IdentifierTreeImpl) constructorInvocation.methodSelect(); - assertThat(i.binding) - .isSameAs(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(constructorInvocation.methodBinding); - assertThat(cu.sema.usages.get(i.binding)) + assertThat(binding(i)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(constructorInvocation)); + assertThat(sema(cu).usages.get(binding(i))) .containsExactlyElementsOf(constructorInvocation.methodSymbol().usages()) .containsOnly(i); } @@ -947,15 +974,15 @@ void statement_constructor_invocation_recovery() { ExpressionStatementTree s = (ExpressionStatementTree) Objects.requireNonNull(m.block()).body().get(0); MethodInvocationTreeImpl constructorInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(constructorInvocation.methodBinding) + assertThat(methodBinding(constructorInvocation)) .isNull(); - assertThat(constructorInvocation.typeBinding) + assertThat(typeBinding(constructorInvocation)) .isNotNull() - .isSameAs(c.typeBinding); + .isSameAs(typeBinding(c)); IdentifierTreeImpl i = (IdentifierTreeImpl) constructorInvocation.methodSelect(); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); - assertThat(cu.sema.usages.get(Objects.requireNonNull(m.methodBinding))) + assertThat(sema(cu).usages.get(Objects.requireNonNull(methodBinding(m)))) .isNull(); } @@ -972,19 +999,19 @@ void statement_super_constructor_invocation() { ExpressionStatementTree s = (ExpressionStatementTree) m.block().body().get(0); MethodInvocationTreeImpl superConstructorInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(superConstructorInvocation.methodBinding) + assertThat(methodBinding(superConstructorInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) superConstructorInvocation.methodSymbol().declaration()).methodBinding) - .isSameAs(superClassConstructor.methodBinding); - assertThat(superConstructorInvocation.typeBinding) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) superConstructorInvocation.methodSymbol().declaration()))) + .isSameAs(methodBinding(superClassConstructor)); + assertThat(typeBinding(superConstructorInvocation)) .isNotNull() - .isSameAs(Objects.requireNonNull(((ClassTreeImpl) superConstructorInvocation.symbolType().symbol().declaration())).typeBinding) - .isSameAs(superClass.typeBinding); + .isSameAs(typeBinding(Objects.requireNonNull(((ClassTreeImpl) superConstructorInvocation.symbolType().symbol().declaration())))) + .isSameAs(typeBinding(superClass)); IdentifierTreeImpl i = (IdentifierTreeImpl) superConstructorInvocation.methodSelect(); - assertThat(i.binding) - .isSameAs(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(superConstructorInvocation.methodBinding); - assertThat(cu.sema.usages.get(i.binding)) + assertThat(binding(i)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(superConstructorInvocation)); + assertThat(sema(cu).usages.get(binding(i))) .containsExactlyElementsOf(superClassConstructor.symbol().usages()) .containsOnly(i); } @@ -1002,15 +1029,15 @@ void statement_super_constructor_invocation_recovery() { ExpressionStatementTree s = (ExpressionStatementTree) Objects.requireNonNull(m.block()).body().get(0); MethodInvocationTreeImpl superConstructorInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(superConstructorInvocation.methodBinding) + assertThat(methodBinding(superConstructorInvocation)) .isNull(); - assertThat(superConstructorInvocation.typeBinding) + assertThat(typeBinding(superConstructorInvocation)) .isNotNull() - .isSameAs(superClass.typeBinding); + .isSameAs(typeBinding(superClass)); IdentifierTreeImpl i = (IdentifierTreeImpl) superConstructorInvocation.methodSelect(); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); - assertThat(cu.sema.usages.get(Objects.requireNonNull(superClassConstructor.methodBinding))) + assertThat(sema(cu).usages.get(Objects.requireNonNull(methodBinding(superClassConstructor)))) .isNull(); } @@ -1020,14 +1047,14 @@ void statement_variable_declaration() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); VariableTreeImpl s = (VariableTreeImpl) m.block().body().get(0); - assertThat(s.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(s.variableBinding)).isSameAs(s); + assertThat(variableBinding(s)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(s))).isSameAs(s); assertThat(s.symbol().isUnknown()).isFalse(); assertThat(s.symbol().name()).isEqualTo("v"); // simulate a variable binding problem - s.variableBinding = null; + clearSymbol(s); assertThat(s.symbol().isUnknown()).isTrue(); } @@ -1039,8 +1066,8 @@ void statement_for() { MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); ForStatementTreeImpl s = (ForStatementTreeImpl) m.block().body().get(0); VariableTreeImpl v = (VariableTreeImpl) s.initializer().get(0); - assertThat(v.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(v.variableBinding)).isSameAs(v); + assertThat(variableBinding(v)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(v))).isSameAs(v); } /** @@ -1052,11 +1079,11 @@ void declaration_import() { JavaTree.ImportTreeImpl i = (JavaTree.ImportTreeImpl) cu.imports().get(0); MemberSelectExpressionTreeImpl qualifiedName = (MemberSelectExpressionTreeImpl) i.qualifiedIdentifier(); IdentifierTreeImpl identifier = (IdentifierTreeImpl) qualifiedName.identifier(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNull(); - assertThat(i.binding) + assertThat(binding(i)) .isNotNull(); - assertThat(cu.sema.typeSymbol((ITypeBinding) i.binding).usages()) + assertThat(sema(cu).typeSymbol((ITypeBinding) binding(i)).usages()) .isEmpty(); } @@ -1064,17 +1091,17 @@ void declaration_import() { void declaration_type() { JavaTree.CompilationUnitTreeImpl cu = test("class C { }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); - assertThat(c.typeBinding).isNotNull(); - assertThat(cu.sema.declarations.get(c.typeBinding)).isSameAs(c); + assertThat(typeBinding(c)).isNotNull(); + assertThat(sema(cu).declarations.get(typeBinding(c))).isSameAs(c); TypeParameterTreeImpl typeParameter = (TypeParameterTreeImpl) c.typeParameters().get(0); - assertThat(typeParameter.typeBinding) + assertThat(typeBinding(typeParameter)) .isNotNull(); assertThat(typeParameter.symbol().isUnknown()).isFalse(); assertThat(typeParameter.symbol().name()).isEqualTo("T"); // simulate a type binding problem - typeParameter.typeBinding = null; + clearType(typeParameter); assertThat(typeParameter.symbol().isUnknown()).isTrue(); } @@ -1154,14 +1181,14 @@ void declaration_type_parameterized() { AbstractTypedTree fieldType = (AbstractTypedTree) ((VariableTreeImpl) c.members().get(0)).type(); IdentifierTreeImpl i = (IdentifierTreeImpl) ((ParameterizedTypeTree) fieldType).type(); - assertThat(cu.sema.usages.get(c.typeBinding)) + assertThat(sema(cu).usages.get(typeBinding(c))) .containsOnly(i); - assertThat(fieldType.typeBinding) + assertThat(typeBinding(fieldType)) .isNotNull(); - assertThat(cu.sema.typeSymbol(fieldType.typeBinding).declaration()) + assertThat(sema(cu).typeSymbol(typeBinding(fieldType)).declaration()) .isSameAs(fieldType.symbolType().symbol().declaration()) .isSameAs(c); - assertThat(cu.sema.typeSymbol(fieldType.typeBinding).usages()) + assertThat(sema(cu).typeSymbol(typeBinding(fieldType)).usages()) .containsExactlyElementsOf(fieldType.symbolType().symbol().usages()) .containsOnly(i); } @@ -1175,17 +1202,17 @@ void declaration_enum_constant() { ClassTree e = (ClassTree) cu.types().get(0); VariableTreeImpl c = (VariableTreeImpl) e.members().get(0); MethodTreeImpl constructor = (MethodTreeImpl) e.members().get(1); - assertThat(c.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(c.variableBinding)) + assertThat(variableBinding(c)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(c))) .isSameAs(c.symbol().declaration()) .isSameAs(c); NewClassTreeImpl initializer = Objects.requireNonNull((NewClassTreeImpl) c.initializer()); IdentifierTreeImpl i = (IdentifierTreeImpl) initializer.getConstructorIdentifier(); - assertThat(i.binding) + assertThat(binding(i)) .isNotNull() - .isSameAs(((MethodTreeImpl) i.symbol().declaration()).methodBinding) - .isSameAs(constructor.methodBinding); - assertThat(cu.sema.usages.get(constructor.methodBinding)) + .isSameAs(methodBinding(((MethodTreeImpl) i.symbol().declaration()))) + .isSameAs(methodBinding(constructor)); + assertThat(sema(cu).usages.get(methodBinding(constructor))) .containsOnly(i); } @@ -1199,7 +1226,7 @@ void declaration_enum_constant_recovered_constructor() { VariableTreeImpl c = (VariableTreeImpl) e.members().get(0); NewClassTreeImpl initializer = Objects.requireNonNull((NewClassTreeImpl) c.initializer()); IdentifierTreeImpl i = (IdentifierTreeImpl) initializer.getConstructorIdentifier(); - assertThat(i.binding).isNull(); + assertThat(binding(i)).isNull(); assertThat(i.symbol().isUnknown()).isTrue(); } @@ -1212,24 +1239,24 @@ void declaration_enum_constant_anonymous() { ClassTree e = (ClassTree) cu.types().get(0); MethodTreeImpl constructor = (MethodTreeImpl) e.members().get(1); VariableTreeImpl c = (VariableTreeImpl) e.members().get(0); - assertThat(c.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(c.variableBinding)) + assertThat(variableBinding(c)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(c))) .isSameAs(c.symbol().declaration()) .isSameAs(c); NewClassTreeImpl initializer = (NewClassTreeImpl) c.initializer(); ClassTreeImpl enumConstantBody = (ClassTreeImpl) initializer.classBody(); - assertThat(enumConstantBody.typeBinding).isNotNull(); - assertThat(cu.sema.declarations.get(enumConstantBody.typeBinding)) + assertThat(typeBinding(enumConstantBody)).isNotNull(); + assertThat(sema(cu).declarations.get(typeBinding(enumConstantBody))) .isSameAs(enumConstantBody.symbol().declaration()) .isSameAs(enumConstantBody); IdentifierTreeImpl identifier = (IdentifierTreeImpl) initializer.getConstructorIdentifier(); - assertThat(identifier.binding) + assertThat(binding(identifier)) .isNotNull() - .isSameAs(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()).methodBinding) - .isSameAs(constructor.methodBinding); - assertThat(cu.sema.usages.get(constructor.methodBinding)) + .isSameAs(methodBinding(Objects.requireNonNull((MethodTreeImpl) identifier.symbol().declaration()))) + .isSameAs(methodBinding(constructor)); + assertThat(sema(cu).usages.get(methodBinding(constructor))) .containsExactlyElementsOf(constructor.symbol().usages()) .containsOnly(identifier); } @@ -1239,8 +1266,8 @@ void declaration_method() { JavaTree.CompilationUnitTreeImpl cu = test("class C { void m() {} }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - assertThat(m.methodBinding).isNotNull(); - assertThat(cu.sema.declarations.get(m.methodBinding)).isSameAs(m); + assertThat(methodBinding(m)).isNotNull(); + assertThat(sema(cu).declarations.get(methodBinding(m))).isSameAs(m); } /** @@ -1256,14 +1283,14 @@ void declaration_method_parameterized() { MethodInvocationTreeImpl methodInvocation = (MethodInvocationTreeImpl) s.expression(); IdentifierTreeImpl i = (IdentifierTreeImpl) methodInvocation.methodSelect(); - assertThat(cu.sema.usages.get(m.methodBinding)) + assertThat(sema(cu).usages.get(methodBinding(m))) .containsOnly(i); - assertThat(methodInvocation.methodBinding) + assertThat(methodBinding(methodInvocation)) .isNotNull(); - assertThat(cu.sema.methodSymbol(methodInvocation.methodBinding).declaration()) + assertThat(sema(cu).methodSymbol(methodBinding(methodInvocation)).declaration()) .isSameAs(methodInvocation.methodSymbol().declaration()) .isSameAs(m); - assertThat(cu.sema.methodSymbol(methodInvocation.methodBinding).usages()) + assertThat(sema(cu).methodSymbol(methodBinding(methodInvocation)).usages()) .containsExactlyElementsOf(methodInvocation.methodSymbol().usages()) .containsOnly(i); } @@ -1281,7 +1308,7 @@ private java.util.Collection> samples() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - assertThat(m.methodBinding).isNull(); + assertThat(methodBinding(m)).isNull(); MethodSymbol recovered = m.symbol(); assertThat(recovered.isUnknown()).isTrue(); @@ -1351,8 +1378,8 @@ void declaration_parameter() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); VariableTreeImpl p = (VariableTreeImpl) m.parameters().get(0); - assertThat(p.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(p.variableBinding)).isSameAs(p); + assertThat(variableBinding(p)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(p))).isSameAs(p); } @Test @@ -1360,8 +1387,8 @@ void declaration_field() { JavaTree.CompilationUnitTreeImpl cu = test("class C { int f; }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); VariableTreeImpl f = (VariableTreeImpl) c.members().get(0); - assertThat(f.variableBinding).isNotNull(); - assertThat(cu.sema.declarations.get(f.variableBinding)).isSameAs(f); + assertThat(variableBinding(f)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(f))).isSameAs(f); } @Test @@ -1373,10 +1400,10 @@ void declaration_lambda_parameter() { MethodInvocationTreeImpl i = (MethodInvocationTreeImpl) s.expression(); LambdaExpressionTree e = (LambdaExpressionTree) i.arguments().get(0); VariableTreeImpl v = (VariableTreeImpl) e.parameters().get(0); - assertThat(v.variableBinding).isNotNull(); + assertThat(variableBinding(v)).isNotNull(); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(cu.sema.declarations.get(v.variableBinding)).isSameAs(v); + assertThat(typeBinding(t)).isNotNull(); + assertThat(sema(cu).declarations.get(variableBinding(v))).isSameAs(v); } @Test @@ -1384,8 +1411,8 @@ void declaration_annotation_member() { JavaTree.CompilationUnitTreeImpl cu = test("@interface A { String m(); }"); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); - assertThat(m.methodBinding).isNotNull(); - assertThat(cu.sema.declarations.get(m.methodBinding)).isSameAs(m); + assertThat(methodBinding(m)).isNotNull(); + assertThat(sema(cu).declarations.get(methodBinding(m))).isSameAs(m); } @ParameterizedTest(name="[{index}] Type bindings of variable v should not be null in \"{0}\"") @@ -1401,7 +1428,7 @@ void test_abstract_type_tree_is_not_null(String source) { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); VariableTreeImpl v = (VariableTreeImpl) c.members().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); } @Test @@ -1410,11 +1437,11 @@ void type_array() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); VariableTreeImpl v = (VariableTreeImpl) c.members().get(0); JavaTree.ArrayTypeTreeImpl t = (JavaTree.ArrayTypeTreeImpl) v.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(t.typeBinding.getDimensions()).isEqualTo(2); + assertThat(typeBinding(t)).isNotNull(); + assertThat(typeBinding(t).getDimensions()).isEqualTo(2); t = (JavaTree.ArrayTypeTreeImpl) t.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(t.typeBinding.getDimensions()).isEqualTo(1); + assertThat(typeBinding(t)).isNotNull(); + assertThat(typeBinding(t).getDimensions()).isEqualTo(1); } @Test @@ -1424,7 +1451,7 @@ void type_wildcard() { VariableTreeImpl v = (VariableTreeImpl) c.members().get(0); ParameterizedTypeTree p = (ParameterizedTypeTree) v.type(); AbstractTypedTree t = ((AbstractTypedTree) p.typeArguments().get(0)); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); } /** @@ -1466,7 +1493,7 @@ void type_try_with_resource() { TryStatementTree s = (TryStatementTree) m.block().body().get(0); VariableTreeImpl v = (VariableTreeImpl) s.resourceList().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); JType t2 = (JType) v.symbol().type(); assertThat(t2).isNotNull(); assertThat(t2.isUnknown()).isTrue(); @@ -1480,7 +1507,7 @@ void type_try_with_resource() { TryStatementTree s = (TryStatementTree) m.block().body().get(0); VariableTreeImpl v = (VariableTreeImpl) s.resourceList().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); Type type = v.symbol().type(); assertThat(type).isNotNull(); assertThat(type.isUnknown()).isTrue(); @@ -1495,7 +1522,7 @@ void type_union() { TryStatementTree s = (TryStatementTree) m.block().body().get(0); VariableTreeImpl v = (VariableTreeImpl) s.catches().get(0).parameter(); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); Type symbolType = t.symbolType(); assertThat(symbolType).isNotNull(); assertThat(symbolType.isUnknown()).isFalse(); @@ -1513,7 +1540,7 @@ void type_intersection_in_cast_expression() { MethodTree m = (MethodTree) c.members().get(0); VariableTreeImpl v = (VariableTreeImpl) m.block().body().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); Type symbolType = t.symbolType(); assertThat(symbolType).isNotNull(); assertThat(symbolType.fullyQualifiedName()).isEqualTo("java.lang.AutoCloseable & java.lang.Cloneable & java.lang.Comparable"); @@ -1554,7 +1581,7 @@ void type_var() { MethodTree m = (MethodTree) c.members().get(0); VariableTreeImpl v = (VariableTreeImpl) m.block().body().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); + assertThat(typeBinding(t)).isNotNull(); } @Test @@ -1563,11 +1590,11 @@ void type_extra_dimensions() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); VariableTreeImpl v = (VariableTreeImpl) c.members().get(0); JavaTree.ArrayTypeTreeImpl t = (JavaTree.ArrayTypeTreeImpl) v.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(t.typeBinding.getDimensions()).isEqualTo(2); + assertThat(typeBinding(t)).isNotNull(); + assertThat(typeBinding(t).getDimensions()).isEqualTo(2); t = (JavaTree.ArrayTypeTreeImpl) t.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(t.typeBinding.getDimensions()).isEqualTo(1); + assertThat(typeBinding(t)).isNotNull(); + assertThat(typeBinding(t).getDimensions()).isEqualTo(1); } @Test @@ -1577,8 +1604,8 @@ void type_vararg() { MethodTree m = (MethodTree) c.members().get(0); VariableTreeImpl v = (VariableTreeImpl) m.parameters().get(0); AbstractTypedTree t = (AbstractTypedTree) v.type(); - assertThat(t.typeBinding).isNotNull(); - assertThat(t.typeBinding.getDimensions()).isEqualTo(2); + assertThat(typeBinding(t)).isNotNull(); + assertThat(typeBinding(t).getDimensions()).isEqualTo(2); } @Test @@ -1586,7 +1613,7 @@ void symbol_unknown() { JavaTree.CompilationUnitTreeImpl cu = test("class A implements UnknownInterface { }"); ClassTree c = (ClassTree) cu.types().get(0); IdentifierTreeImpl i = (IdentifierTreeImpl) c.superInterfaces().get(0); - Symbol s = cu.sema.typeSymbol(Objects.requireNonNull((ITypeBinding) i.binding)); + Symbol s = sema(cu).typeSymbol(Objects.requireNonNull((ITypeBinding) binding(i))); assertThat(s.isUnknown()) .isEqualTo(i.symbol().isUnknown()) .isTrue(); @@ -1607,10 +1634,10 @@ void annotation_on_type() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); VariableTreeImpl parameter = (VariableTreeImpl) m.parameters().get(0); - assertThat(cu.sema.variableSymbol(parameter.variableBinding).metadata().annotations().size()) + assertThat(sema(cu).variableSymbol(variableBinding(parameter)).metadata().annotations().size()) .isEqualTo(parameter.symbol().metadata().annotations().size()) .isEqualTo(1); - assertThat(JUtils.parameterAnnotations(cu.sema.methodSymbol(Objects.requireNonNull(m.methodBinding)), 0).annotations().size()) + assertThat(JUtils.parameterAnnotations(sema(cu).methodSymbol(Objects.requireNonNull(methodBinding(m))), 0).annotations().size()) .isEqualTo(JUtils.parameterAnnotations(m.symbol(), 0).annotations().size()) .isEqualTo(1); } @@ -1652,7 +1679,7 @@ void statement_break() { .isInstanceOf(Symbol.LabelSymbol.class) .isInstanceOf(Symbol.class) .isSameAs(l.labelSymbol); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); assertThat(i.symbol()) @@ -1683,7 +1710,7 @@ void statement_continue() { assertThat(l.labelSymbol) .isNotNull() .isSameAs(i.labelSymbol); - assertThat(i.binding) + assertThat(binding(i)) .isNull(); assertThat(l.labelSymbol.declaration) .isSameAs(i.labelSymbol.declaration) @@ -1705,7 +1732,7 @@ void nested() { assertThat(l1.labelSymbol) .isNotNull() .isSameAs(i1.labelSymbol); - assertThat(i1.binding) + assertThat(binding(i1)) .isNull(); assertThat(l1.labelSymbol.declaration) .isSameAs(i1.labelSymbol.declaration) @@ -1723,7 +1750,7 @@ void nested() { assertThat(l2.labelSymbol) .isNotNull() .isSameAs(i2.labelSymbol); - assertThat(i2.binding) + assertThat(binding(i2)) .isNull(); assertThat(l2.labelSymbol.declaration) .isSameAs(i2.labelSymbol.declaration) diff --git a/java-frontend/src/test/java/org/sonar/java/model/JParserTest.java b/java-frontend/src/test/java/org/sonar/java/model/JParserTest.java index 57d8dfeb19b..0ac663b4bcb 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JParserTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JParserTest.java @@ -150,7 +150,7 @@ void err() { void unknown_types_are_collected() { // import org.foo missing, type Bar unknown CompilationUnitTree cut = test("import org.foo.Bar;\n class Foo {\n void foo(Bar b) {}\n }\n"); - assertThat(((CompilationUnitTreeImpl) cut).sema.undefinedTypes.stream().map(Object::toString)) + assertThat(((CompilationUnitTreeImpl) cut).undefinedTypes().stream().map(Object::toString)) .containsExactlyInAnyOrder("The import org.foo cannot be resolved", "Bar cannot be resolved to a type"); } @@ -158,7 +158,7 @@ void unknown_types_are_collected() { void warnings_are_collected() { // import org.foo missing, type Bar unknown CompilationUnitTree cut = test("import java.util.List;\n import java.util.ArrayList;\n class Foo {\n void foo(Bar b) {}\n }\n"); - assertThat(((CompilationUnitTreeImpl) cut).sema.undefinedTypes.stream().map(Object::toString)) + assertThat(((CompilationUnitTreeImpl) cut).undefinedTypes().stream().map(Object::toString)) .containsExactlyInAnyOrder("Bar cannot be resolved to a type"); } diff --git a/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java b/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java index bb37c9cfca1..64291977a35 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JSymbolTest.java @@ -51,6 +51,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.methodBinding; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; +import static org.sonar.java.model.EcjTestSupport.variableBinding; import static org.sonar.java.model.assertions.SymbolAssert.assertThat; class JSymbolTest { @@ -62,13 +66,13 @@ void name() { MethodTreeImpl constructor = (MethodTreeImpl) c.members().get(0); MethodTreeImpl method = (MethodTreeImpl) c.members().get(1); - assertThat(cu.sema.typeSymbol(c.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c))) .hasSameNameAs(c.symbol()) .hasName("C"); - assertThat(cu.sema.methodSymbol(constructor.methodBinding)) + assertThat(sema(cu).methodSymbol(methodBinding(constructor))) .hasSameNameAs(constructor.symbol()) .hasName(""); - assertThat(cu.sema.methodSymbol(method.methodBinding)) + assertThat(sema(cu).methodSymbol(methodBinding(method))) .hasSameNameAs(method.symbol()) .hasName("m"); } @@ -84,39 +88,39 @@ void owner() { ClassTreeImpl c3 = (ClassTreeImpl) m.block().body().get(0); VariableTreeImpl u = (VariableTreeImpl) c3.members().get(0); - assertThat(cu.sema.typeSymbol(c1.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c1))) .as("of top-level class") - .hasOwner(cu.sema.packageSymbol(c1.typeBinding.getPackage())); + .hasOwner(sema(cu).packageSymbol(typeBinding(c1).getPackage())); - assertThat(cu.sema.typeSymbol(c2.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c2))) .as("of nested class") - .hasOwner(cu.sema.typeSymbol(c1.typeBinding)); + .hasOwner(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.typeSymbol(c3.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c3))) .as("of local class") - .hasOwner(cu.sema.methodSymbol(m.methodBinding)); + .hasOwner(sema(cu).methodSymbol(methodBinding(m))); - assertThat(cu.sema.methodSymbol(m.methodBinding)) + assertThat(sema(cu).methodSymbol(methodBinding(m))) .as("of method") - .hasOwner(cu.sema.typeSymbol(c1.typeBinding)); + .hasOwner(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.variableSymbol(f.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(f))) .as("of field") - .hasOwner(cu.sema.typeSymbol(c1.typeBinding)); + .hasOwner(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.variableSymbol(p.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(p))) .as("of method parameter") - .hasOwner(cu.sema.methodSymbol(m.methodBinding)); + .hasOwner(sema(cu).methodSymbol(methodBinding(m))); - assertThat(cu.sema.typeSymbol(p.variableBinding.getType())) + assertThat(sema(cu).typeSymbol(variableBinding(p).getType())) .as("of type int") .hasOwner(Symbol.ROOT_PACKAGE) .hasSameHashCodeAs(p.type().symbolType().symbol().hashCode()); - assertThat(cu.sema.packageSymbol(null)) + assertThat(sema(cu).packageSymbol(null)) .isEqualTo(Symbol.ROOT_PACKAGE); - JType uType = cu.sema.type(u.variableBinding.getType()); + JType uType = sema(cu).type(variableBinding(u).getType()); Symbol.TypeSymbol uTypeSymbol = uType.symbol(); assertThat(uType.isUnknown()).isTrue(); assertThat(uTypeSymbol.isUnknown()).isTrue(); @@ -161,13 +165,13 @@ void owner_local_record() { ClassTreeImpl r = (ClassTreeImpl) m.block().body().get(0); VariableTreeImpl p = (VariableTreeImpl) r.recordComponents().get(0); - assertThat(cu.sema.variableSymbol(p.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(p))) .as("of record component") - .hasOwner(cu.sema.typeSymbol(r.typeBinding)); + .hasOwner(sema(cu).typeSymbol(typeBinding(r))); - assertThat(cu.sema.methodSymbol(m.methodBinding)) + assertThat(sema(cu).methodSymbol(methodBinding(m))) .as("of method") - .hasOwner(cu.sema.typeSymbol(c1.typeBinding)); + .hasOwner(sema(cu).typeSymbol(typeBinding(c1))); } @Test @@ -210,8 +214,8 @@ void primitive_type_hash_code() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); VariableTreeImpl uField = (VariableTreeImpl) c.members().get(0); VariableTreeImpl vField = (VariableTreeImpl) c.members().get(1); - assertThat(cu.sema.typeSymbol(uField.variableBinding.getType())) - .hasSameHashCodeAs(cu.sema.typeSymbol(vField.variableBinding.getType())); + assertThat(sema(cu).typeSymbol(variableBinding(uField).getType())) + .hasSameHashCodeAs(sema(cu).typeSymbol(variableBinding(vField).getType())); } @Test @@ -224,29 +228,29 @@ void enclosingClass() { VariableTreeImpl p = (VariableTreeImpl) m.parameters().get(0); ClassTreeImpl c3 = (ClassTreeImpl) m.block().body().get(0); - assertThat(cu.sema.typeSymbol(c1.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c1))) .as("of top-level class") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.typeSymbol(c2.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c2))) .as("of nested class") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.typeSymbol(c3.typeBinding)) + assertThat(sema(cu).typeSymbol(typeBinding(c3))) .as("of local class") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.methodSymbol(m.methodBinding)) + assertThat(sema(cu).methodSymbol(methodBinding(m))) .as("of method") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.variableSymbol(f.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(f))) .as("of field") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.variableSymbol(p.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(p))) .as("of method parameter") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); } @Test @@ -257,13 +261,13 @@ void record_enclosingClass() { ClassTreeImpl c2 = (ClassTreeImpl) c1.members().get(0); VariableTreeImpl p = (VariableTreeImpl) c2.recordComponents().get(0); - assertThat(cu.sema.variableSymbol(f.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(f))) .as("of field") - .hasEnclosingClass(cu.sema.typeSymbol(c1.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c1))); - assertThat(cu.sema.variableSymbol(p.variableBinding)) + assertThat(sema(cu).variableSymbol(variableBinding(p))) .as("of method parameter") - .hasEnclosingClass(cu.sema.typeSymbol(c2.typeBinding)); + .hasEnclosingClass(sema(cu).typeSymbol(typeBinding(c2))); } @Test @@ -290,16 +294,16 @@ private void variable_in_class_initializer(boolean isStatic) { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); BlockTreeImpl b = (BlockTreeImpl) c.members().get(1); VariableTreeImpl v = (VariableTreeImpl) b.body().get(0); - Symbol.TypeSymbol t = cu.sema.typeSymbol(c.typeBinding); + Symbol.TypeSymbol t = sema(cu).typeSymbol(typeBinding(c)); - JVariableSymbol variableSymbol = cu.sema.variableSymbol(v.variableBinding); + JVariableSymbol variableSymbol = sema(cu).variableSymbol(variableBinding(v)); assertThat(variableSymbol).isSameAs(v.symbol()); JInitializerBlockSymbol initializerBlock = (JInitializerBlockSymbol) variableSymbol.owner(); if (isStatic) { - assertThat(initializerBlock).isSameAs(cu.sema.staticInitializerBlockSymbol((JTypeSymbol) c.symbol())); + assertThat(initializerBlock).isSameAs(sema(cu).staticInitializerBlockSymbol((JTypeSymbol) c.symbol())); } else { - assertThat(initializerBlock).isSameAs(cu.sema.initializerBlockSymbol((JTypeSymbol) c.symbol())); + assertThat(initializerBlock).isSameAs(sema(cu).initializerBlockSymbol((JTypeSymbol) c.symbol())); } assertThat(variableSymbol).hasEnclosingClass(t); @@ -357,17 +361,17 @@ void type() { VariableTreeImpl field = (VariableTreeImpl) c.members().get(0); MethodTreeImpl method = (MethodTreeImpl) c.members().get(1); - assertThat(cu.sema.typeSymbol(c.typeBinding).owner()) + assertThat(sema(cu).typeSymbol(typeBinding(c)).owner()) .isOfSameTypeAs(c.symbol().owner()) .isOfUnknownType(); - assertThat(cu.sema.typeSymbol(c.typeBinding)) - .isOfType(cu.sema.type(c.typeBinding)); + assertThat(sema(cu).typeSymbol(typeBinding(c))) + .isOfType(sema(cu).type(typeBinding(c))); - assertThat(cu.sema.variableSymbol(field.variableBinding)) - .isOfType(cu.sema.type(field.variableBinding.getType())); + assertThat(sema(cu).variableSymbol(variableBinding(field))) + .isOfType(sema(cu).type(variableBinding(field).getType())); - assertThat(cu.sema.methodSymbol(method.methodBinding)).isOfUnknownType(); + assertThat(sema(cu).methodSymbol(methodBinding(method))).isOfUnknownType(); } @Test @@ -397,8 +401,8 @@ void var_type_as_local_variable() { assertThat(variable.symbol()) .isOfType("java.util.ArrayList"); - assertThat(cu.sema.variableSymbol(variable.variableBinding)) - .isOfType(cu.sema.type(variable.variableBinding.getType())); + assertThat(sema(cu).variableSymbol(variableBinding(variable))) + .isOfType(sema(cu).type(variableBinding(variable).getType())); } @Test @@ -460,30 +464,30 @@ void parse() { @Test void isPackageSymbol() { - JTypeSymbol c1Symbol = cu.sema.typeSymbol(c1.typeBinding); + JTypeSymbol c1Symbol = sema(cu).typeSymbol(typeBinding(c1)); assertThat(c1Symbol.isPackageSymbol()).isFalse(); assertThat(c1Symbol.owner().isPackageSymbol()).isTrue(); } @Test void isTypeSymbol() { - assertThat(cu.sema.typeSymbol(c1.typeBinding).isTypeSymbol()).isTrue(); - assertThat(cu.sema.methodSymbol(foo.methodBinding).isTypeSymbol()).isFalse(); - assertThat(cu.sema.variableSymbol(f.variableBinding).isTypeSymbol()).isFalse(); + assertThat(sema(cu).typeSymbol(typeBinding(c1)).isTypeSymbol()).isTrue(); + assertThat(sema(cu).methodSymbol(methodBinding(foo)).isTypeSymbol()).isFalse(); + assertThat(sema(cu).variableSymbol(variableBinding(f)).isTypeSymbol()).isFalse(); } @Test void isMethodSymbol() { - assertThat(cu.sema.typeSymbol(c1.typeBinding).isMethodSymbol()).isFalse(); - assertThat(cu.sema.methodSymbol(foo.methodBinding).isMethodSymbol()).isTrue(); - assertThat(cu.sema.variableSymbol(f.variableBinding).isMethodSymbol()).isFalse(); + assertThat(sema(cu).typeSymbol(typeBinding(c1)).isMethodSymbol()).isFalse(); + assertThat(sema(cu).methodSymbol(methodBinding(foo)).isMethodSymbol()).isTrue(); + assertThat(sema(cu).variableSymbol(variableBinding(f)).isMethodSymbol()).isFalse(); } @Test void isVariableSymbol() { - assertThat(cu.sema.typeSymbol(c1.typeBinding).isVariableSymbol()).isFalse(); - assertThat(cu.sema.methodSymbol(foo.methodBinding).isVariableSymbol()).isFalse(); - assertThat(cu.sema.variableSymbol(f.variableBinding).isVariableSymbol()).isTrue(); + assertThat(sema(cu).typeSymbol(typeBinding(c1)).isVariableSymbol()).isFalse(); + assertThat(sema(cu).methodSymbol(methodBinding(foo)).isVariableSymbol()).isFalse(); + assertThat(sema(cu).variableSymbol(variableBinding(f)).isVariableSymbol()).isTrue(); } } @@ -598,8 +602,8 @@ void isInterface() { "interface I1 { } " + "class C1 { }"); - assertThat(cu.sema.typeSymbol(((ClassTreeImpl) cu.types().get(0)).typeBinding).isInterface()).isTrue(); - assertThat(cu.sema.typeSymbol(((ClassTreeImpl) cu.types().get(1)).typeBinding).isInterface()).isFalse(); + assertThat(sema(cu).typeSymbol(typeBinding(((ClassTreeImpl) cu.types().get(0)))).isInterface()).isTrue(); + assertThat(sema(cu).typeSymbol(typeBinding(((ClassTreeImpl) cu.types().get(1)))).isInterface()).isFalse(); } @Test @@ -607,20 +611,20 @@ void isEnum() { JavaTree.CompilationUnitTreeImpl cu = test("class C1 { int a; void foo() { }} enum E1 { ACONST; }"); ClassTreeImpl c1 = (ClassTreeImpl) cu.types().get(0); - JTypeSymbol c1Symbol = cu.sema.typeSymbol(c1.typeBinding); + JTypeSymbol c1Symbol = sema(cu).typeSymbol(typeBinding(c1)); assertThat(c1Symbol.isEnum()).isFalse(); - JVariableSymbol a = cu.sema.variableSymbol(((VariableTreeImpl) c1.members().get(0)).variableBinding); + JVariableSymbol a = sema(cu).variableSymbol(variableBinding(((VariableTreeImpl) c1.members().get(0)))); assertThat(a.isEnum()).isFalse(); - JMethodSymbol foo = cu.sema.methodSymbol(((MethodTreeImpl) c1.members().get(1)).methodBinding); + JMethodSymbol foo = sema(cu).methodSymbol(methodBinding(((MethodTreeImpl) c1.members().get(1)))); assertThat(foo.isEnum()).isFalse(); ClassTreeImpl e1 = (ClassTreeImpl) cu.types().get(1); - JTypeSymbol e1Symbol = cu.sema.typeSymbol(e1.typeBinding); + JTypeSymbol e1Symbol = sema(cu).typeSymbol(typeBinding(e1)); assertThat(e1Symbol.isEnum()).isTrue(); - JVariableSymbol aconst = cu.sema.variableSymbol(((EnumConstantTreeImpl) e1.members().get(0)).variableBinding); + JVariableSymbol aconst = sema(cu).variableSymbol(variableBinding(((EnumConstantTreeImpl) e1.members().get(0)))); assertThat(aconst.isEnum()).isTrue(); } @@ -634,12 +638,12 @@ private JMethodSymbol bar(JavaTree.CompilationUnitTreeImpl cu) { private JMethodSymbol method(JavaTree.CompilationUnitTreeImpl cu, int memberIndex) { ClassTreeImpl c1 = (ClassTreeImpl) cu.types().get(0); - return cu.sema.methodSymbol(((MethodTreeImpl) c1.members().get(memberIndex)).methodBinding); + return sema(cu).methodSymbol(methodBinding(((MethodTreeImpl) c1.members().get(memberIndex)))); } private JVariableSymbol variable(JavaTree.CompilationUnitTreeImpl cu, int memberIndex) { ClassTreeImpl c1 = (ClassTreeImpl) cu.types().get(0); - return cu.sema.variableSymbol(((VariableTreeImpl) c1.members().get(memberIndex)).variableBinding); + return sema(cu).variableSymbol(variableBinding(((VariableTreeImpl) c1.members().get(memberIndex)))); } } diff --git a/java-frontend/src/test/java/org/sonar/java/model/JTypeSymbolTest.java b/java-frontend/src/test/java/org/sonar/java/model/JTypeSymbolTest.java index b45c8478bda..654cbfada69 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JTypeSymbolTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JTypeSymbolTest.java @@ -28,36 +28,40 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.methodBinding; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; +import static org.sonar.java.model.EcjTestSupport.variableBinding; class JTypeSymbolTest { @Test void superClass() { JavaTree.CompilationUnitTreeImpl cu = test("interface I { } class C implements I { } class C2 extends C { }"); - ITypeBinding javaLangObject = Objects.requireNonNull(cu.sema.resolveType("java.lang.Object")); - ITypeBinding i = Objects.requireNonNull(((ClassTreeImpl) cu.types().get(0)).typeBinding); - ITypeBinding c = Objects.requireNonNull(((ClassTreeImpl) cu.types().get(1)).typeBinding); - ITypeBinding c2 = Objects.requireNonNull(((ClassTreeImpl) cu.types().get(2)).typeBinding); + ITypeBinding javaLangObject = Objects.requireNonNull(sema(cu).resolveType("java.lang.Object")); + ITypeBinding i = Objects.requireNonNull(typeBinding(((ClassTreeImpl) cu.types().get(0)))); + ITypeBinding c = Objects.requireNonNull(typeBinding(((ClassTreeImpl) cu.types().get(1)))); + ITypeBinding c2 = Objects.requireNonNull(typeBinding(((ClassTreeImpl) cu.types().get(2)))); assertAll( // for java.lang.Object () -> - assertThat(cu.sema.typeSymbol(javaLangObject).superClass()) + assertThat(sema(cu).typeSymbol(javaLangObject).superClass()) .isNull(), // for interfaces () -> - assertThat(cu.sema.typeSymbol(i).superClass()) - .isSameAs(cu.sema.type(javaLangObject)), + assertThat(sema(cu).typeSymbol(i).superClass()) + .isSameAs(sema(cu).type(javaLangObject)), // for classes () -> - assertThat(cu.sema.typeSymbol(c).superClass()) - .isSameAs(cu.sema.type(javaLangObject)), + assertThat(sema(cu).typeSymbol(c).superClass()) + .isSameAs(sema(cu).type(javaLangObject)), () -> - assertThat(cu.sema.typeSymbol(c2).superClass()) - .isSameAs(cu.sema.type(c)), + assertThat(sema(cu).typeSymbol(c2).superClass()) + .isSameAs(sema(cu).type(c)), // for arrays () -> - assertThat(cu.sema.typeSymbol(javaLangObject.createArrayType(1)).superClass()) - .isSameAs(cu.sema.type(javaLangObject)) + assertThat(sema(cu).typeSymbol(javaLangObject.createArrayType(1)).superClass()) + .isSameAs(sema(cu).type(javaLangObject)) ); } @@ -66,21 +70,21 @@ void testSuperClassWhenSemanticCanNotResolveObjectType() { var cu = test("interface I{}"); var i = (ClassTreeImpl) cu.types().get(0); - JSema sematicThatCanNotResolveObjectType = spy(cu.sema); + JSema sematicThatCanNotResolveObjectType = spy(sema(cu)); when(sematicThatCanNotResolveObjectType.resolveType("java.lang.Object")).thenReturn(null); - var typeSymbol = new JTypeSymbol(sematicThatCanNotResolveObjectType, i.typeBinding); + var typeSymbol = new JTypeSymbol(sematicThatCanNotResolveObjectType, typeBinding(i)); assertThat(typeSymbol.superClass()).isSameAs(Type.UNKNOWN); } @Test void interfaces() { JavaTree.CompilationUnitTreeImpl cu = test("interface I { } class C implements I { }"); - ITypeBinding i = Objects.requireNonNull(((ClassTreeImpl) cu.types().get(0)).typeBinding); - ITypeBinding c = Objects.requireNonNull(((ClassTreeImpl) cu.types().get(1)).typeBinding); - assertThat(cu.sema.typeSymbol(i).interfaces()) + ITypeBinding i = Objects.requireNonNull(typeBinding(((ClassTreeImpl) cu.types().get(0)))); + ITypeBinding c = Objects.requireNonNull(typeBinding(((ClassTreeImpl) cu.types().get(1)))); + assertThat(sema(cu).typeSymbol(i).interfaces()) .isEmpty(); - assertThat(cu.sema.typeSymbol(c).interfaces()) - .containsOnly(cu.sema.type(i)); + assertThat(sema(cu).typeSymbol(c).interfaces()) + .containsOnly(sema(cu).type(i)); } @Test @@ -90,37 +94,37 @@ void memberSymbols() { VariableTreeImpl f = (VariableTreeImpl) c.members().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(1); ClassTreeImpl n = (ClassTreeImpl) c.members().get(2); - assertThat(cu.sema.typeSymbol(c.typeBinding).memberSymbols()) + assertThat(sema(cu).typeSymbol(typeBinding(c)).memberSymbols()) .containsOnly( - cu.sema.variableSymbol(f.variableBinding), - cu.sema.methodSymbol(m.methodBinding), - cu.sema.typeSymbol(n.typeBinding) + sema(cu).variableSymbol(variableBinding(f)), + sema(cu).methodSymbol(methodBinding(m)), + sema(cu).typeSymbol(typeBinding(n)) ); } @Test void superTypesTest(){ JavaTree.CompilationUnitTreeImpl cu = test("interface I { } class C implements I { } class C2 extends C { }"); - ITypeBinding javaLangObject = Objects.requireNonNull(cu.sema.resolveType("java.lang.Object")); - assertThat(cu.sema.typeSymbol(javaLangObject).superTypes()).isEmpty(); + ITypeBinding javaLangObject = Objects.requireNonNull(sema(cu).resolveType("java.lang.Object")); + assertThat(sema(cu).typeSymbol(javaLangObject).superTypes()).isEmpty(); ClassTreeImpl interfaceI = (ClassTreeImpl) cu.types().get(0); - JTypeSymbol interfaceITypeSymbol = cu.sema.typeSymbol(interfaceI.typeBinding); + JTypeSymbol interfaceITypeSymbol = sema(cu).typeSymbol(typeBinding(interfaceI)); assertThat(interfaceITypeSymbol.superTypes()).isEmpty(); assertThat(interfaceITypeSymbol.superTypes()).isEmpty(); // repeat call to cover cache ClassTreeImpl classC = (ClassTreeImpl) cu.types().get(1); - JTypeSymbol classCTypeSymbol = cu.sema.typeSymbol(classC.typeBinding); - assertThat(classCTypeSymbol.superTypes()).containsExactly(interfaceITypeSymbol.type(), cu.sema.type(javaLangObject)); + JTypeSymbol classCTypeSymbol = sema(cu).typeSymbol(typeBinding(classC)); + assertThat(classCTypeSymbol.superTypes()).containsExactly(interfaceITypeSymbol.type(), sema(cu).type(javaLangObject)); ClassTreeImpl classC2 = (ClassTreeImpl) cu.types().get(2); - JTypeSymbol classC2TypeSymbol = cu.sema.typeSymbol(classC2.typeBinding); - assertThat(classC2TypeSymbol.superTypes()).containsExactly(classCTypeSymbol.type() ,interfaceITypeSymbol.type(), cu.sema.type(javaLangObject)); + JTypeSymbol classC2TypeSymbol = sema(cu).typeSymbol(typeBinding(classC2)); + assertThat(classC2TypeSymbol.superTypes()).containsExactly(classCTypeSymbol.type() ,interfaceITypeSymbol.type(), sema(cu).type(javaLangObject)); - ITypeBinding brokenTypeBinding = spy(classC2.typeBinding); + ITypeBinding brokenTypeBinding = spy(typeBinding(classC2)); when(brokenTypeBinding.isRecovered()).thenReturn(true); - JTypeSymbol brokenTypeSymbol = new JTypeSymbol(cu.sema, brokenTypeBinding); + JTypeSymbol brokenTypeSymbol = new JTypeSymbol(sema(cu), brokenTypeBinding); assertThat(brokenTypeSymbol.superTypes()).isEmpty(); } @@ -129,8 +133,8 @@ void outermostClassTest() { JavaTree.CompilationUnitTreeImpl cu = test("class C { class N {} }"); ClassTreeImpl outerClass = (ClassTreeImpl) cu.types().get(0); ClassTreeImpl innerClass = (ClassTreeImpl) outerClass.members().get(0); - JTypeSymbol innerClassSymbol = cu.sema.typeSymbol(innerClass.typeBinding); - JTypeSymbol outerClassSymbol = cu.sema.typeSymbol(outerClass.typeBinding); + JTypeSymbol innerClassSymbol = sema(cu).typeSymbol(typeBinding(innerClass)); + JTypeSymbol outerClassSymbol = sema(cu).typeSymbol(typeBinding(outerClass)); assertThat(innerClassSymbol.outermostClass()).isSameAs(outerClassSymbol); assertThat(innerClassSymbol.outermostClass()).isSameAs(outerClassSymbol); } @@ -155,11 +159,11 @@ private static JTypeSymbol getJTypeSymbolFromClassText(String classText, boolean JavaTree.CompilationUnitTreeImpl cu = test(classText); ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); if(isUnknown){ - ITypeBinding brokenTypeBinding = spy(c.typeBinding); + ITypeBinding brokenTypeBinding = spy(typeBinding(c)); when(brokenTypeBinding.isRecovered()).thenReturn(true); - return new JTypeSymbol(cu.sema, brokenTypeBinding); + return new JTypeSymbol(sema(cu), brokenTypeBinding); } - return new JTypeSymbol(cu.sema, c.typeBinding); + return new JTypeSymbol(sema(cu), typeBinding(c)); } private static JavaTree.CompilationUnitTreeImpl test(String source) { diff --git a/java-frontend/src/test/java/org/sonar/java/model/JTypeTest.java b/java-frontend/src/test/java/org/sonar/java/model/JTypeTest.java index 1d8e96d60fb..45352af96a1 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JTypeTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JTypeTest.java @@ -42,6 +42,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; import static org.sonar.java.model.assertions.TypeAssert.assertThat; class JTypeTest { @@ -95,7 +97,7 @@ void isParametrizedType() { ExpressionStatementTreeImpl s = (ExpressionStatementTreeImpl) Objects.requireNonNull(m.block()).body().get(0); AbstractTypedTree e = Objects.requireNonNull((AbstractTypedTree) s.expression()); - assertThat(cu.sema.type(e.typeBinding).isParameterized()) + assertThat(sema(cu).type(typeBinding(e)).isParameterized()) .isEqualTo(e.symbolType().isParameterized()) .isTrue(); } @@ -108,11 +110,11 @@ void typeArguments() { ExpressionStatementTreeImpl s = (ExpressionStatementTreeImpl) Objects.requireNonNull(m.block()).body().get(0); AbstractTypedTree e = Objects.requireNonNull((AbstractTypedTree) s.expression()); - assertThat(cu.sema.type(e.typeBinding).typeArguments()) + assertThat(sema(cu).type(typeBinding(e)).typeArguments()) .hasToString(e.symbolType().typeArguments().toString()) .hasToString("[Integer, String]"); - assertThat(cu.sema.type(c.typeBinding).typeArguments()) + assertThat(sema(cu).type(typeBinding(c)).typeArguments()) .isEqualTo(c.symbol().type().typeArguments()) .isEmpty(); } @@ -235,11 +237,11 @@ void name_of_parameterized_type() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); AbstractTypedTree e = Objects.requireNonNull((AbstractTypedTree) m.returnType()); - JType parameterizedType = cu.sema.type(Objects.requireNonNull(e.typeBinding)); + JType parameterizedType = sema(cu).type(Objects.requireNonNull(typeBinding(e))); assertThat(parameterizedType) .hasSameNameAs(e.symbolType()) - .hasSameNameAs(cu.sema.typeSymbol(e.typeBinding).type()) + .hasSameNameAs(sema(cu).typeSymbol(typeBinding(e)).type()) .is("java.util.List") .hasName("List"); } @@ -250,10 +252,10 @@ void name_of_type_variable() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl m = (MethodTreeImpl) c.members().get(0); AbstractTypedTree e = Objects.requireNonNull((AbstractTypedTree) m.returnType()); - JType typeVariable = cu.sema.type(Objects.requireNonNull(e.typeBinding)); + JType typeVariable = sema(cu).type(Objects.requireNonNull(typeBinding(e))); assertThat(typeVariable) .hasSameNameAs(e.symbolType()) - .hasSameNameAs(cu.sema.typeSymbol(e.typeBinding).type()) + .hasSameNameAs(sema(cu).typeSymbol(typeBinding(e)).type()) .hasName("T") .is(e.symbolType().fullyQualifiedName()) .is("T"); @@ -308,10 +310,10 @@ void null_type() { AbstractTypedTree primitive = (AbstractTypedTree) m.parameters().get(0).type(); AbstractTypedTree array = (AbstractTypedTree) m.parameters().get(1).type(); - JType nullType = cu.sema.type(Objects.requireNonNull(e.typeBinding)); - JType classType = cu.sema.type(Objects.requireNonNull(c.typeBinding)); - JType primitiveType = cu.sema.type(Objects.requireNonNull(primitive.typeBinding)); - JType arrayType = cu.sema.type(Objects.requireNonNull(array.typeBinding)); + JType nullType = sema(cu).type(Objects.requireNonNull(typeBinding(e))); + JType classType = sema(cu).type(Objects.requireNonNull(typeBinding(c))); + JType primitiveType = sema(cu).type(Objects.requireNonNull(typeBinding(primitive))); + JType arrayType = sema(cu).type(Objects.requireNonNull(typeBinding(array))); assertThat(nullType) .hasSameNameAs(e.symbolType()) @@ -348,9 +350,9 @@ void wildcard() { JavaTree.WildcardTreeImpl w1 = (JavaTree.WildcardTreeImpl) p.typeArguments().get(0); JavaTree.WildcardTreeImpl w2 = (JavaTree.WildcardTreeImpl) p.typeArguments().get(1); JavaTree.WildcardTreeImpl w3 = (JavaTree.WildcardTreeImpl) p.typeArguments().get(2); - JType wildcardType1 = cu.sema.type(Objects.requireNonNull(w1.typeBinding)); - JType wildcardType2 = cu.sema.type(Objects.requireNonNull(w2.typeBinding)); - JType wildcardType3 = cu.sema.type(Objects.requireNonNull(w3.typeBinding)); + JType wildcardType1 = sema(cu).type(Objects.requireNonNull(typeBinding(w1))); + JType wildcardType2 = sema(cu).type(Objects.requireNonNull(typeBinding(w2))); + JType wildcardType3 = sema(cu).type(Objects.requireNonNull(typeBinding(w3))); assertThat(wildcardType1) .isEqualTo(wildcardType2) diff --git a/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java b/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java index 8a5fd6263b4..16496a21611 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JUtilsTest.java @@ -28,6 +28,7 @@ import org.sonar.java.model.expression.TypeCastExpressionTreeImpl; import org.sonar.java.model.statement.ExpressionStatementTreeImpl; import org.sonar.java.model.statement.ReturnStatementTreeImpl; +import org.sonar.plugins.java.api.semantic.Sema; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.SymbolMetadata; import org.sonar.plugins.java.api.semantic.Type; @@ -37,12 +38,17 @@ import org.sonar.plugins.java.api.tree.TypeParameterTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.methodBinding; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.typeBinding; +import static org.sonar.java.model.EcjTestSupport.variableBinding; class JUtilsTest { - static final JSema SEMA = test("").sema; + static final JSema SEMA = sema(test("")); private static final Type OBJECT_TYPE = SEMA.type(SEMA.resolveType("java.lang.Object")); @Test @@ -51,12 +57,15 @@ void semantic_helpers_use_the_existing_api_interfaces() { Type parent = mock(Type.class); when(type.directSuperTypes()).thenReturn(Set.of(parent)); assertThat(JUtils.directSuperTypes(type)).containsExactly(parent); + assertThat(JUtils.directSuperTypes(mock(Type.class, CALLS_REAL_METHODS))).isEmpty(); Symbol.MethodSymbol method = mock(Symbol.MethodSymbol.class); when(method.defaultValue()).thenReturn(42); assertThat(JUtils.defaultValue(method)).isEqualTo(42); assertThat(Type.UNKNOWN.directSuperTypes()).isEmpty(); assertThat(Symbol.MethodSymbol.UNKNOWN_METHOD.defaultValue()).isNull(); assertThat(new JInitializerBlockSymbol(Symbol.TypeSymbol.UNKNOWN_TYPE, false).defaultValue()).isNull(); + Sema sema = name -> Type.UNKNOWN; + sema.getEnvironmentCleaner().run(); } @Nested @@ -156,14 +165,14 @@ class IsNullType { void nullType() { ReturnStatementTreeImpl s = (ReturnStatementTreeImpl) m1.block().body().get(0); AbstractTypedTree e = (AbstractTypedTree) s.expression(); - assertThat(cu.sema.type(e.typeBinding).isNullType()) + assertThat(sema(cu).type(typeBinding(e)).isNullType()) .isEqualTo(e.symbolType().isNullType()) .isTrue(); } @Test void a_non_null_type_is_not_null_type() { - assertThat(cu.sema.type(m1.methodBinding.getReturnType()).isNullType()) + assertThat(sema(cu).type(methodBinding(m1).getReturnType()).isNullType()) .isEqualTo(m1.returnType().symbolType().isNullType()) .isFalse(); } @@ -171,7 +180,7 @@ void a_non_null_type_is_not_null_type() { @Test void unresolved_type_is_not_null_type() { MethodTreeImpl m2 = nthMethod(c, 1); - assertThat(cu.sema.type(m2.methodBinding.getReturnType()).isNullType()) + assertThat(sema(cu).type(methodBinding(m2).getReturnType()).isNullType()) .isEqualTo(m2.returnType().symbolType().isNullType()) .isFalse(); } @@ -194,14 +203,14 @@ class C { @Test void intersection_type() { TypeCastExpressionTreeImpl e = (TypeCastExpressionTreeImpl) f.initializer(); - assertThat(JUtils.isIntersectionType(cu.sema.type(e.typeBinding))) + assertThat(JUtils.isIntersectionType(sema(cu).type(typeBinding(e)))) .isEqualTo(JUtils.isIntersectionType(e.symbolType())) .isTrue(); } @Test void non_intersection_type() { - assertThat(JUtils.isIntersectionType(cu.sema.type(f.variableBinding.getType()))) + assertThat(JUtils.isIntersectionType(sema(cu).type(variableBinding(f).getType()))) .isEqualTo(JUtils.isIntersectionType(f.symbol().type())) .isFalse(); } @@ -209,7 +218,7 @@ void non_intersection_type() { @Test void unresolved_type_is_not_an_intersection_type() { VariableTreeImpl u = nthField(c, 1); - assertThat(JUtils.isIntersectionType(cu.sema.type(u.variableBinding.getType()))) + assertThat(JUtils.isIntersectionType(sema(cu).type(variableBinding(u).getType()))) .isEqualTo(JUtils.isIntersectionType(u.symbol().type())) .isFalse(); } @@ -223,7 +232,7 @@ class IsTypeVar { @Test void type_var() { VariableTreeImpl t = firstField(c); - assertThat(cu.sema.type(t.variableBinding.getType()).isTypeVar()) + assertThat(sema(cu).type(variableBinding(t).getType()).isTypeVar()) .isEqualTo(t.symbol().type().isTypeVar()) .isTrue(); } @@ -236,7 +245,7 @@ void simple_type_is_not_a_type_var() { @Test void unresolved_type_is_not_a_type_var() { VariableTreeImpl u = nthField(c, 1); - assertThat(cu.sema.type(u.variableBinding.getType()).isTypeVar()) + assertThat(sema(cu).type(variableBinding(u).getType()).isTypeVar()) .isEqualTo(u.symbol().type().isTypeVar()) .isFalse(); } @@ -400,22 +409,22 @@ void constantValue() { JavaTree.CompilationUnitTreeImpl specificCu = test("interface I { short SHORT = 42; char CHAR = 42; byte BYTE = 42; boolean BOOLEAN = false; }"); ClassTreeImpl specificC = firstClass(specificCu); - Symbol.VariableSymbol shortConstant = specificCu.sema.variableSymbol(firstField(specificC).variableBinding); + Symbol.VariableSymbol shortConstant = sema(specificCu).variableSymbol(variableBinding(firstField(specificC))); assertThat(shortConstant.constantValue().orElseThrow(AssertionError::new)) .isInstanceOf(Integer.class) .isEqualTo(42); - Symbol.VariableSymbol charConstant = specificCu.sema.variableSymbol(nthField(specificC, 1).variableBinding); + Symbol.VariableSymbol charConstant = sema(specificCu).variableSymbol(variableBinding(nthField(specificC, 1))); assertThat(charConstant.constantValue().orElseThrow(AssertionError::new)) .isInstanceOf(Integer.class) .isEqualTo(42); - Symbol.VariableSymbol byteConstant = specificCu.sema.variableSymbol(nthField(specificC, 2).variableBinding); + Symbol.VariableSymbol byteConstant = sema(specificCu).variableSymbol(variableBinding(nthField(specificC, 2))); assertThat(byteConstant.constantValue().orElseThrow(AssertionError::new)) .isInstanceOf(Integer.class) .isEqualTo(42); - Symbol.VariableSymbol booleanConstant = specificCu.sema.variableSymbol(nthField(specificC, 3).variableBinding); + Symbol.VariableSymbol booleanConstant = sema(specificCu).variableSymbol(variableBinding(nthField(specificC, 3))); assertThat(booleanConstant.constantValue().orElseThrow(AssertionError::new)) .isInstanceOf(Boolean.class) .isEqualTo(Boolean.FALSE); @@ -721,13 +730,13 @@ void isParametrizedMethod() { ExpressionStatementTreeImpl s = (ExpressionStatementTreeImpl) m.block().body().get(0); MethodInvocationTreeImpl methodInvocation = (MethodInvocationTreeImpl) s.expression(); - assertThat(cu.sema.methodSymbol(m.methodBinding).isParametrizedMethod()) + assertThat(sema(cu).methodSymbol(methodBinding(m)).isParametrizedMethod()) .isEqualTo(m.symbol().isParametrizedMethod()) - .isEqualTo(m.methodBinding.isGenericMethod()) + .isEqualTo(methodBinding(m).isGenericMethod()) .isTrue(); - assertThat(cu.sema.methodSymbol(methodInvocation.methodBinding).isParametrizedMethod()) + assertThat(sema(cu).methodSymbol(methodBinding(methodInvocation)).isParametrizedMethod()) .isEqualTo(methodInvocation.methodSymbol().isParametrizedMethod()) - .isEqualTo(methodInvocation.methodBinding.isParameterizedMethod()) + .isEqualTo(methodBinding(methodInvocation).isParameterizedMethod()) .isTrue(); } diff --git a/java-frontend/src/test/java/org/sonar/java/model/JVariableSymbolTest.java b/java-frontend/src/test/java/org/sonar/java/model/JVariableSymbolTest.java index a3f2c17bdfe..cf569dcfa8a 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JVariableSymbolTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JVariableSymbolTest.java @@ -26,6 +26,8 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.java.model.EcjTestSupport.sema; +import static org.sonar.java.model.EcjTestSupport.variableBinding; class JVariableSymbolTest { @@ -35,10 +37,10 @@ void isLocalVariable() { ClassTreeImpl c = (ClassTreeImpl) cu.types().get(0); MethodTreeImpl method = (MethodTreeImpl) c.members().get(0); VariableTreeImpl localVariable = (VariableTreeImpl) method.block().body().get(0); - JVariableSymbol variableSymbol = cu.sema.variableSymbol(localVariable.variableBinding); + JVariableSymbol variableSymbol = sema(cu).variableSymbol(variableBinding(localVariable)); assertThat(variableSymbol.isLocalVariable()).isTrue(); VariableTreeImpl classVariable = (VariableTreeImpl) c.members().get(1); - variableSymbol = cu.sema.variableSymbol(classVariable.variableBinding); + variableSymbol = sema(cu).variableSymbol(variableBinding(classVariable)); assertThat(variableSymbol.isLocalVariable()).isFalse(); } @@ -61,11 +63,11 @@ void isParameter() { MethodTreeImpl method = (MethodTreeImpl) c.members().get(0); VariableTreeImpl localVariable = (VariableTreeImpl) method.block().body().get(0); - JVariableSymbol variableSymbol = cu.sema.variableSymbol(localVariable.variableBinding); + JVariableSymbol variableSymbol = sema(cu).variableSymbol(variableBinding(localVariable)); assertThat(variableSymbol.isParameter()).isFalse(); VariableTreeImpl parameter = (VariableTreeImpl) method.parameters().get(0); - variableSymbol = cu.sema.variableSymbol(parameter.variableBinding); + variableSymbol = sema(cu).variableSymbol(variableBinding(parameter)); assertThat(variableSymbol.isParameter()).isTrue(); } diff --git a/java-frontend/src/test/java/org/sonar/java/model/KeywordSuperTest.java b/java-frontend/src/test/java/org/sonar/java/model/KeywordSuperTest.java index 5b56b3cfe22..f1ef8a10b4c 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/KeywordSuperTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/KeywordSuperTest.java @@ -23,6 +23,8 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.java.model.EcjTestSupport.clearSymbol; +import static org.sonar.java.model.EcjTestSupport.clearType; class KeywordSuperTest { @@ -43,8 +45,8 @@ void test_symbol_and_symbolType() { assertThat(keywordSuper.symbolType().name()).isEqualTo("Object"); // simulate a binding problem - keywordSuper.binding = null; - keywordSuper.typeBinding = null; + clearSymbol(keywordSuper); + clearType(keywordSuper); assertThat(keywordSuper.symbol().isUnknown()).isTrue(); assertThat(keywordSuper.symbolType().isUnknown()).isTrue(); diff --git a/java-frontend/src/test/java/org/sonar/java/model/KeywordThisTest.java b/java-frontend/src/test/java/org/sonar/java/model/KeywordThisTest.java index eddc5a8beab..c9a9d639565 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/KeywordThisTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/KeywordThisTest.java @@ -23,6 +23,8 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.java.model.EcjTestSupport.clearSymbol; +import static org.sonar.java.model.EcjTestSupport.clearType; class KeywordThisTest { @@ -43,8 +45,8 @@ void test_symbol_and_symbolType() { assertThat(keywordThis.symbolType().name()).isEqualTo("A"); // simulate a binding problem - keywordThis.binding = null; - keywordThis.typeBinding = null; + clearSymbol(keywordThis); + clearType(keywordThis); assertThat(keywordThis.symbol().isUnknown()).isTrue(); assertThat(keywordThis.symbolType().isUnknown()).isTrue(); diff --git a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java index 7451d522b5b..cf0d6e33345 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/SymbolsTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.sonar.java.model.EcjTestSupport.sema; class SymbolsTest { @@ -203,7 +204,7 @@ private static void assertCommonProperties(Symbol unknownSymbol) { @Test void testIsAnnotation() { - JSema sema = ((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse("")).sema; + JSema sema = sema(((JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse(""))); Type objectType = sema.type(sema.resolveType("java.lang.Object")); JavaTree.CompilationUnitTreeImpl cu = (JavaTree.CompilationUnitTreeImpl) JParserTestUtils.parse("@interface Anno { Unknown u; }"); ClassTreeImpl anno = (ClassTreeImpl) cu.types().get(0); diff --git a/java-frontend/src/test/java/org/sonar/java/model/declaration/ClassTreeImplTest.java b/java-frontend/src/test/java/org/sonar/java/model/declaration/ClassTreeImplTest.java index 22c2165bebc..4e811aefdaf 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/declaration/ClassTreeImplTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/declaration/ClassTreeImplTest.java @@ -29,6 +29,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.java.model.EcjTestSupport.clearType; import static org.sonar.java.model.assertions.TreeAssert.assertThat; class ClassTreeImplTest { @@ -37,7 +38,7 @@ class ClassTreeImplTest { void get_symbol_return_unknown_when_type_binding_is_null() { var classTree = (ClassTreeImpl) JParserTestUtils.parse("class A {}").types().get(0); // simulate source code with type binding problems - classTree.typeBinding = null; + clearType(classTree); assertThat(classTree.symbol().isUnknown()).isTrue(); } From bf825e5c20fdc689baaa98ce2f0535957376a9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Mon, 2 Nov 2026 16:29:23 +0100 Subject: [PATCH 4/5] Fix formatting --- .../sonar/java/model/EcjSymbolMetadata.java | 3 +- .../org/sonar/java/model/EcjTreeBindings.java | 14 ++++------ .../org/sonar/java/model/JSymbolMetadata.java | 4 +-- .../java/model/declaration/ClassTreeImpl.java | 13 ++++----- .../model/declaration/VariableTreeImpl.java | 17 ++++++----- .../expression/LambdaExpressionTreeImpl.java | 15 +++++----- .../expression/MethodInvocationTreeImpl.java | 11 ++++---- .../sonar/java/model/ClassesLayoutTest.java | 28 ++----------------- .../sonar/java/model/JSymbolMetadataTest.java | 18 ++++++------ 9 files changed, 46 insertions(+), 77 deletions(-) diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java b/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java index 8e97a4f7005..85cc1830144 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjSymbolMetadata.java @@ -55,8 +55,7 @@ private static JSymbolMetadata ofTypeArg(JSema sema, Symbol owner, ITypeBinding return metadata(sema, owner, symbolAnnotations, parameterMetaDatas, true); } - private static JSymbolMetadata metadata(JSema sema, Symbol owner, IAnnotationBinding[] bindings, - JSymbolMetadata[] parameters, boolean typeArgument) { + private static JSymbolMetadata metadata(JSema sema, Symbol owner, IAnnotationBinding[] bindings, JSymbolMetadata[] parameters, boolean typeArgument) { return new JSymbolMetadata(owner, () -> Arrays.stream(bindings).map(sema::annotation).collect(Collectors.toList()), parameters, typeArgument); } diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java b/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java index 045d24b4eac..c4faa059c42 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjTreeBindings.java @@ -74,17 +74,14 @@ static IBinding binding(Object tree) { static void setType(JSema sema, JavaTree tree, @Nullable ITypeBinding binding) { switch (tree) { case AbstractTypedTree t -> t.type = sema.typeSupplier(binding); - case ClassTreeImpl t -> t.typeSymbol = binding == null ? SemanticValues.UNKNOWN_TYPE_SYMBOL - : new EcjSemanticAttachment<>(binding, sema::typeSymbol); + case ClassTreeImpl t -> t.typeSymbol = binding == null ? SemanticValues.UNKNOWN_TYPE_SYMBOL : new EcjSemanticAttachment<>(binding, sema::typeSymbol); case TypeParameterTreeImpl t -> t.typeSymbol = sema.symbolSupplier(binding); default -> throw new IllegalArgumentException("No type attachment: " + tree.getClass()); } if (tree instanceof KeywordThis t) { - t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL - : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).thisSymbol); + t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).thisSymbol); } else if (tree instanceof KeywordSuper t) { - t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL - : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).superSymbol); + t.symbolSupplier = binding == null ? SemanticValues.UNKNOWN_SYMBOL : new EcjSemanticAttachment<>(binding, b -> sema.typeSymbol(b).superSymbol); } } @@ -95,8 +92,9 @@ static void setSymbol(JSema sema, JavaTree tree, @Nullable IBinding binding) { case LambdaExpressionTreeImpl t -> t.methodSymbol = sema.methodSupplier((IMethodBinding) binding); case VariableTreeImpl t -> t.variableSymbol = sema.symbolSupplier(binding); case IdentifierTreeImpl t -> t.symbolSupplier = sema.symbolSupplier(binding); - case JavaTree.ImportTreeImpl t -> t.symbolSupplier = binding == null || binding.getKind() == IBinding.PACKAGE - || binding.getKind() == IBinding.MODULE ? () -> null : sema.symbolSupplier(binding); + case JavaTree.ImportTreeImpl t -> t.symbolSupplier = (binding == null || binding.getKind() == IBinding.PACKAGE || binding.getKind() == IBinding.MODULE) + ? () -> null + : sema.symbolSupplier(binding); default -> throw new IllegalArgumentException("No symbol attachment: " + tree.getClass()); } } diff --git a/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java b/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java index 422a97356e5..bed4fab6112 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JSymbolMetadata.java @@ -21,7 +21,6 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -66,8 +65,7 @@ final class JSymbolMetadata implements SymbolMetadata { this(symbol, () -> annotations, new JSymbolMetadata[0], false); } - JSymbolMetadata(Symbol symbol, Supplier> annotations, - JSymbolMetadata[] parameterMetas, boolean isTypeArgMetadata) { + JSymbolMetadata(Symbol symbol, Supplier> annotations, JSymbolMetadata[] parameterMetas, boolean isTypeArgMetadata) { this.symbol = symbol; this.annotationSource = annotations; this.parameterMetas = parameterMetas; diff --git a/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java index 754a97fe91f..b94ab583a31 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/declaration/ClassTreeImpl.java @@ -16,12 +16,15 @@ */ package org.sonar.java.model.declaration; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nullable; import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl; import org.sonar.java.ast.parser.TypeParameterListTreeImpl; -import org.sonar.java.model.SemanticValues; -import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.InternalSyntaxToken; import org.sonar.java.model.JavaTree; +import org.sonar.java.model.SemanticValues; import org.sonar.java.model.expression.IdentifierTreeImpl; import org.sonar.plugins.java.api.location.Position; import org.sonar.plugins.java.api.semantic.Symbol; @@ -35,11 +38,7 @@ import org.sonar.plugins.java.api.tree.TypeParameters; import org.sonar.plugins.java.api.tree.TypeTree; import org.sonar.plugins.java.api.tree.VariableTree; - -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; +import org.sonarsource.analyzer.commons.collections.ListUtils; public class ClassTreeImpl extends JavaTree implements ClassTree { diff --git a/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java index 958c4b12357..8b6f1c58019 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/declaration/VariableTreeImpl.java @@ -17,13 +17,17 @@ package org.sonar.java.model.declaration; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import org.sonar.java.model.SemanticValues; -import org.sonar.plugins.java.api.semantic.Symbol; -import org.sonarsource.analyzer.commons.collections.ListUtils; +import java.util.Objects; +import java.util.function.Supplier; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.java.model.InternalSyntaxToken; import org.sonar.java.model.JavaTree; +import org.sonar.java.model.SemanticValues; import org.sonar.java.model.expression.IdentifierTreeImpl; +import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.ExpressionTree; import org.sonar.plugins.java.api.tree.IdentifierTree; import org.sonar.plugins.java.api.tree.InferedTypeTree; @@ -33,12 +37,7 @@ import org.sonar.plugins.java.api.tree.TreeVisitor; import org.sonar.plugins.java.api.tree.TypeTree; import org.sonar.plugins.java.api.tree.VariableTree; - -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.Objects; -import java.util.function.Supplier; +import org.sonarsource.analyzer.commons.collections.ListUtils; public class VariableTreeImpl extends JavaTree implements VariableTree { private ModifiersTree modifiers; diff --git a/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java index 19d378eaf4a..12f1ef903ac 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/expression/LambdaExpressionTreeImpl.java @@ -17,23 +17,22 @@ package org.sonar.java.model.expression; import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nullable; import org.sonar.java.cfg.CFG; -import org.sonar.java.model.SemanticValues; -import org.sonar.plugins.java.api.semantic.Symbol; -import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.java.model.InternalSyntaxToken; +import org.sonar.java.model.SemanticValues; import org.sonar.plugins.java.api.cfg.ControlFlowGraph; +import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.BlockTree; import org.sonar.plugins.java.api.tree.LambdaExpressionTree; import org.sonar.plugins.java.api.tree.SyntaxToken; import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TreeVisitor; import org.sonar.plugins.java.api.tree.VariableTree; - -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; +import org.sonarsource.analyzer.commons.collections.ListUtils; public class LambdaExpressionTreeImpl extends AssessableExpressionTree implements LambdaExpressionTree { diff --git a/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java b/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java index aafd8a5f797..7e302a8a521 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java +++ b/java-frontend/src/main/java/org/sonar/java/model/expression/MethodInvocationTreeImpl.java @@ -17,10 +17,13 @@ package org.sonar.java.model.expression; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import javax.annotation.Nullable; import org.sonar.java.ast.parser.ArgumentListTreeImpl; import org.sonar.java.model.SemanticValues; -import org.sonarsource.analyzer.commons.collections.ListUtils; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.tree.Arguments; import org.sonar.plugins.java.api.tree.ExpressionTree; @@ -30,11 +33,7 @@ import org.sonar.plugins.java.api.tree.Tree; import org.sonar.plugins.java.api.tree.TreeVisitor; import org.sonar.plugins.java.api.tree.TypeArguments; - -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.Objects; -import java.util.function.Supplier; +import org.sonarsource.analyzer.commons.collections.ListUtils; public class MethodInvocationTreeImpl extends AssessableExpressionTree implements MethodInvocationTree { diff --git a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java index ebc0270a67c..0bb713c9c92 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/ClassesLayoutTest.java @@ -16,6 +16,9 @@ */ package org.sonar.java.model; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashSet; import org.junit.jupiter.api.Test; import org.sonar.java.model.declaration.VariableTreeImpl; import org.sonar.java.model.expression.IdentifierTreeImpl; @@ -23,15 +26,6 @@ import org.sonar.java.model.expression.MemberSelectExpressionTreeImpl; import org.sonar.java.model.expression.MethodInvocationTreeImpl; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -43,22 +37,6 @@ */ class ClassesLayoutTest { - @Test - void shared_model_and_contracts_have_no_compiler_imports() throws Exception { - Path model = Path.of("src/main/java/org/sonar/java/model"); - Set sharedFiles = Set.of("AbstractTypedTree.java", "JavaTree.java", "KeywordThis.java", "KeywordSuper.java", - "TypeParameterTreeImpl.java", "JUtils.java", "JSymbolMetadata.java", "JSymbolMetadataNullabilityHelper.java", - "JProblem.java", "JWarning.java", "SemanticValues.java"); - try (Stream files = Files.walk(model)) { - for (Path file : files.filter(path -> path.toString().endsWith(".java")).toList()) { - if (sharedFiles.contains(file.getFileName().toString()) || List.of("declaration", "expression", "pattern").contains(file.getParent().getFileName().toString())) { - assertThat(Files.readAllLines(file).stream().filter(line -> line.startsWith("import "))).as(file.toString()) - .noneMatch(line -> line.contains("org.eclipse.jdt")); - } - } - } - } - @Test void token() { assertThat(countFields(InternalSyntaxToken.class)).isEqualTo(7); diff --git a/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java b/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java index 1597e9e24a5..38584ff963a 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JSymbolMetadataTest.java @@ -57,6 +57,15 @@ class JSymbolMetadataTest { + private static final Path NULLABILITY_SOURCE_DIR = JParserTestUtils.CHECKS_TEST_DIR + .resolve(Paths.get("src", "main", "java", "annotations", "nullability")); + + private static final Pattern NULLABILITY_ID_PATTERN = Pattern.compile("id\\d++" + + "_type_(?NO_ANNOTATION|UNKNOWN|STRONG_NULLABLE|WEAK_NULLABLE|NON_NULL)" + + "(?:_level_(?UNKNOWN|PACKAGE|CLASS|METHOD|VARIABLE))?" + + "(?_meta)?" + + "(?:_line_(?empty|\\d++))?"); + @Test void flattened_annotations_are_independent_of_exposed_metadata_collections() { Symbol owner = mock(Symbol.class); @@ -72,15 +81,6 @@ void flattened_annotations_are_independent_of_exposed_metadata_collections() { assertThat(outer.annotations()).containsExactly(outerAnnotation, argumentAnnotation); } - private static final Path NULLABILITY_SOURCE_DIR = JParserTestUtils.CHECKS_TEST_DIR - .resolve(Paths.get("src", "main", "java", "annotations", "nullability")); - - private static final Pattern NULLABILITY_ID_PATTERN = Pattern.compile("id\\d++" + - "_type_(?NO_ANNOTATION|UNKNOWN|STRONG_NULLABLE|WEAK_NULLABLE|NON_NULL)" + - "(?:_level_(?UNKNOWN|PACKAGE|CLASS|METHOD|VARIABLE))?" + - "(?_meta)?" + - "(?:_line_(?empty|\\d++))?"); - @Test void should_convert_annotation_values() { JavaTree.CompilationUnitTreeImpl cu = test("import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @interface A { }"); From 2d2821fd7ef128eabb67c8427ca09bc2728d39e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Coet?= Date: Tue, 3 Nov 2026 11:08:50 +0100 Subject: [PATCH 5/5] Address review comment --- .../main/java/org/sonar/java/model/EcjDiagnosticProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java b/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java index 4539f4241c6..eee98dedecc 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java +++ b/java-frontend/src/main/java/org/sonar/java/model/EcjDiagnosticProvider.java @@ -58,7 +58,7 @@ private static JWarning convert(IProblem problem, LineColumnConverter positions) for (JProblem.Type type : JProblem.Type.values()) { if (problem.getID() == IDS.get(type)) { LineColumnConverter.Pos start = positions.toPos(problem.getSourceStart()); - LineColumnConverter.Pos end = positions.toPos(problem.getSourceStart()); + LineColumnConverter.Pos end = positions.toPos(problem.getSourceEnd()); return new JWarning(problem.getMessage(), type, start.line(), start.columnOffset(), end.line(), end.columnOffset()); } }