Next batch of JDT reconcilers
This commit is contained in:
@@ -62,6 +62,7 @@ import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnec
|
||||
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorService;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessLiveDataProvider;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
@@ -241,8 +242,8 @@ public class BootLanguageServerBootApp {
|
||||
return new CompilationUnitCache(params.projectFinder, server, params.projectObserver);
|
||||
}
|
||||
|
||||
@Bean JdtReconciler jdtReconciler(CompilationUnitCache cuCache, BootJavaConfig config, SimpleLanguageServer server) {
|
||||
return new JdtReconciler(cuCache, server.getQuickfixRegistry(), config);
|
||||
@Bean JdtReconciler jdtReconciler(CompilationUnitCache cuCache, BootJavaConfig config, SimpleLanguageServer server, JdtAstReconciler[] reconcilers) {
|
||||
return new JdtReconciler(cuCache, config, reconcilers);
|
||||
}
|
||||
|
||||
@Bean SpringXMLCompletionEngine xmlCompletionEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, BootJavaConfig config) {
|
||||
@@ -378,9 +379,9 @@ public class BootLanguageServerBootApp {
|
||||
@Bean
|
||||
ModulithService modulithService(SimpleLanguageServer server, JavaProjectFinder projectFinder,
|
||||
ProjectObserver projectObserver, SpringSymbolIndex springIndex,
|
||||
Optional<BootJavaProjectReconcilerScheduler> projectReconcileScheduler, BootJavaReconcileEngine reconciler,
|
||||
BootJavaReconcileEngine reconciler,
|
||||
BootJavaConfig config) {
|
||||
return new ModulithService(server, projectFinder, projectObserver, springIndex, reconciler, projectReconcileScheduler, config);
|
||||
return new ModulithService(server, projectFinder, projectObserver, springIndex, reconciler, config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.app;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.AddConfigurationIfBeansPresentReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationNodeReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.AutowiredFieldIntoConstructorParameterReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.BeanMethodNotPublicReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.Boot3NotSupportedTypeReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.ModulithTypeReferenceViolationReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.NoAutowiredOnConstructorReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.NoRepoAnnotationReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.NoRequestMappingAnnotationReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.PreciseBeanTypeReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.UnnecessarySpringExtensionReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.WebSecurityConfigurerAdapterReconciler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class JdtConfig {
|
||||
|
||||
@Bean AnnotationNodeReconciler annotationNodeReconciler(BootJavaConfig config) {
|
||||
return new AnnotationNodeReconciler(config);
|
||||
}
|
||||
|
||||
@Bean BeanMethodNotPublicReconciler beanMethodNotPublicReconciler(SimpleLanguageServer server) {
|
||||
return new BeanMethodNotPublicReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean AddConfigurationIfBeansPresentReconciler addConfigurationIfBeansPresentReconciler(SimpleLanguageServer server) {
|
||||
return new AddConfigurationIfBeansPresentReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean AutowiredFieldIntoConstructorParameterReconciler autowiredFieldIntoConstructorParameterReconciler(SimpleLanguageServer server) {
|
||||
return new AutowiredFieldIntoConstructorParameterReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean Boot3NotSupportedTypeReconciler boot3NotSupportedTypeReconciler() {
|
||||
return new Boot3NotSupportedTypeReconciler();
|
||||
}
|
||||
|
||||
@Bean NoAutowiredOnConstructorReconciler noAutowiredOnConstructorReconciler(SimpleLanguageServer server) {
|
||||
return new NoAutowiredOnConstructorReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean WebSecurityConfigurerAdapterReconciler webSecurityConfigurerAdapterReconciler(SimpleLanguageServer server) {
|
||||
return new WebSecurityConfigurerAdapterReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean PreciseBeanTypeReconciler preciseBeanTypeReconciler(SimpleLanguageServer server) {
|
||||
return new PreciseBeanTypeReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean NoRequestMappingAnnotationReconciler noRequestMappingAnnotationReconciler(SimpleLanguageServer server) {
|
||||
return new NoRequestMappingAnnotationReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean ModulithTypeReferenceViolationReconciler modulithTypeReferenceViolationReconciler() {
|
||||
return new ModulithTypeReferenceViolationReconciler();
|
||||
}
|
||||
|
||||
@Bean NoRepoAnnotationReconciler noRepoAnnotationReconciler(SimpleLanguageServer server) {
|
||||
return new NoRepoAnnotationReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean UnnecessarySpringExtensionReconciler unnecessarySpringExtensionReconciler(SimpleLanguageServer server) {
|
||||
return new UnnecessarySpringExtensionReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,7 +59,6 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration classDecl) {
|
||||
if (isApplicableClass(cu, classDecl)) {
|
||||
|
||||
SimpleName nameAst = classDecl.getName();
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), PROBLEM_LABEL,
|
||||
nameAst.getStartPosition(), nameAst.getLength());
|
||||
|
||||
@@ -21,11 +21,9 @@ import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SpelExpressionReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
@@ -50,28 +48,12 @@ public class JdtReconciler implements JavaReconciler {
|
||||
|
||||
private final CompilationUnitCache compilationUnitCache;
|
||||
private final JdtAstReconciler[] reconcilers;
|
||||
private final SpelExpressionReconciler spelExpressionReconciler;
|
||||
private BootJavaConfig config;
|
||||
|
||||
public JdtReconciler(CompilationUnitCache compilationUnitCache, QuickfixRegistry quickfixRegistry, BootJavaConfig config) {
|
||||
public JdtReconciler(CompilationUnitCache compilationUnitCache, BootJavaConfig config, JdtAstReconciler[] reconcilers) {
|
||||
this.compilationUnitCache = compilationUnitCache;
|
||||
this.config = config;
|
||||
config.addListener(evt -> setSpelExpressionSyntaxValidationEnabled(config.isSpelExpressionValidationEnabled()));
|
||||
this.spelExpressionReconciler = new SpelExpressionReconciler();
|
||||
|
||||
this.reconcilers = new JdtAstReconciler[] {
|
||||
new AnnotationNodeReconciler(config),
|
||||
new BeanMethodNotPublicReconciler(quickfixRegistry),
|
||||
new AddConfigurationIfBeansPresentReconciler(quickfixRegistry),
|
||||
new AutowiredFieldIntoConstructorParameterReconciler(quickfixRegistry),
|
||||
new Boot3NotSupportedTypeReconciler(),
|
||||
new NoAutowiredOnConstructorReconciler(quickfixRegistry),
|
||||
new WebSecurityConfigurerAdapterReconciler(quickfixRegistry)
|
||||
};
|
||||
}
|
||||
|
||||
public void setSpelExpressionSyntaxValidationEnabled(boolean spelExpressionValidationEnabled) {
|
||||
this.spelExpressionReconciler.setEnabled(spelExpressionValidationEnabled);
|
||||
this.reconcilers = reconcilers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ImportDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleType;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.modulith.AppModules;
|
||||
import org.springframework.ide.vscode.boot.modulith.ModulithService;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
|
||||
public class ModulithTypeReferenceViolationReconciler implements JdtAstReconciler, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext appContext;
|
||||
|
||||
@Override
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector,
|
||||
boolean isCompleteAst) throws RequiredCompleteAstException {
|
||||
Path sourceFile = Paths.get(docUri);
|
||||
if (IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath())
|
||||
.anyMatch(f -> sourceFile.startsWith(f.toPath()))) {
|
||||
ModulithService modulithService = appContext.getBean(ModulithService.class);
|
||||
AppModules appModules = modulithService.getModulesData(project);
|
||||
|
||||
if (appModules != null) {
|
||||
final String packageName = cu.getPackage().getName().getFullyQualifiedName();
|
||||
cu.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(ImportDeclaration node) {
|
||||
if (!node.isOnDemand()) {
|
||||
String typeFqName = node.getName().getFullyQualifiedName();
|
||||
appModules.getModuleNotExposingType(packageName, getBinaryName(typeFqName)).ifPresent(module -> {
|
||||
problemCollector.accept(new ReconcileProblemImpl(getProblemType(),
|
||||
"Cannot use type in this package. Type is not exposed in module '"
|
||||
+ module.name() + "'.",
|
||||
node.getName().getStartPosition(), node.getName().getLength()));
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(SimpleType node) {
|
||||
if (node.getName().isQualifiedName()) {
|
||||
appModules.getModuleNotExposingType(packageName, getBinaryName(node.getName().getFullyQualifiedName())).ifPresent(module -> {
|
||||
problemCollector.accept(new ReconcileProblemImpl(getProblemType(),
|
||||
"Cannot use type in this package. Type is not exposed in module '"
|
||||
+ module.name() + "'.",
|
||||
node.getName().getStartPosition(), node.getName().getLength()));
|
||||
});
|
||||
} else if (node.getName().isSimpleName()) {
|
||||
String typeName = node.getName().getFullyQualifiedName();
|
||||
for (Object i : cu.imports()) {
|
||||
ImportDeclaration importDecl = (ImportDeclaration) i;
|
||||
if (importDecl.isOnDemand()) {
|
||||
appModules.getModuleNotExposingType(packageName, getBinaryName(importDecl.getName().getFullyQualifiedName() + "." + typeName)).ifPresent(module -> {
|
||||
problemCollector.accept(new ReconcileProblemImpl(getProblemType(),
|
||||
"Cannot use type in this package. Type is not exposed in module '"
|
||||
+ module.name() + "'.",
|
||||
node.getName().getStartPosition(), node.getName().getLength()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBinaryName(String fqName) {
|
||||
String pkgName = ModulithService.getPackageNameFromTypeFQName(fqName);
|
||||
if (pkgName.length() < fqName.length() - 1) {
|
||||
String typeName = fqName.substring(pkgName.length() + 1);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(pkgName);
|
||||
sb.append('.');
|
||||
sb.append(typeName.replace('.', '$'));
|
||||
return sb.toString();
|
||||
}
|
||||
return fqName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
return ModulithService.isModulithDependentProject(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProblemType getProblemType() {
|
||||
return Boot3JavaProblemType.MODULITH_TYPE_REF_VIOLATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.appContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MarkerAnnotation;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.openrewrite.java.spring.NoRepoAnnotationOnRepoInterface;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class NoRepoAnnotationReconciler implements JdtAstReconciler {
|
||||
|
||||
private static final String ID = NoRepoAnnotationOnRepoInterface.class.getName();
|
||||
private static final String LABEL = "Remove Unnecessary @Repository";
|
||||
private static final String INTERFACE_REPOSITORY = "org.springframework.data.repository.Repository";
|
||||
|
||||
private QuickfixRegistry registry;
|
||||
|
||||
public NoRepoAnnotationReconciler(QuickfixRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector,
|
||||
boolean isCompleteAst) throws RequiredCompleteAstException {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration typeDecl) {
|
||||
if (typeDecl.isInterface()) {
|
||||
for (Object o : typeDecl.modifiers()) {
|
||||
if (o instanceof Annotation) {
|
||||
Annotation a = (Annotation) o;
|
||||
if (isApplicableRepoAnnotation(a)) {
|
||||
ITypeBinding type = typeDecl.resolveBinding();
|
||||
if (type != null && isRepo(type)) {
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), LABEL, a.getStartPosition(), a.getLength());
|
||||
String uri = docUri.toASCIIString();
|
||||
RewriteQuickFixUtils.setRewriteFixes(registry, problem, List.of(
|
||||
// new FixDescriptor(ID, List.of(uri), LABEL)
|
||||
// .withRangeScope(RewriteQuickFixUtils.createOpenRewriteRange(cu, typeDecl))
|
||||
// .withRecipeScope(RecipeScope.NODE),
|
||||
new FixDescriptor(ID, List.of(uri),
|
||||
RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE))
|
||||
.withRecipeScope(RecipeScope.FILE),
|
||||
new FixDescriptor(ID, List.of(uri),
|
||||
RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(typeDecl);
|
||||
}
|
||||
|
||||
private boolean isApplicableRepoAnnotation(Annotation a) {
|
||||
if (a instanceof MarkerAnnotation || (a.isNormalAnnotation() && ((NormalAnnotation) a).properties().isEmpty())) {
|
||||
String typeName = a.getTypeName().getFullyQualifiedName();
|
||||
if (Annotations.REPOSITORY.equals(typeName)) {
|
||||
return true;
|
||||
} else if (typeName.endsWith("Repository")) {
|
||||
ITypeBinding type = a.resolveTypeBinding();
|
||||
if (type != null && Annotations.REPOSITORY.equals(type.getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRepo(ITypeBinding t) {
|
||||
if (INTERFACE_REPOSITORY.equals(t.getQualifiedName())) {
|
||||
return true;
|
||||
} else {
|
||||
for (ITypeBinding st : t.getInterfaces()) {
|
||||
if (isRepo(st)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
return springBootVersionGreaterOrEqual(2, 0, 0).test(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boot2JavaProblemType getProblemType() {
|
||||
return Boot2JavaProblemType.JAVA_REPOSITORY;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MarkerAnnotation;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.openrewrite.java.spring.NoRequestMappingAnnotation;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class NoRequestMappingAnnotationReconciler implements JdtAstReconciler {
|
||||
|
||||
private static final String LABEL = "Replace @RequestMapping with specific @GetMapping, @PostMapping etc.";
|
||||
private static final String ID = NoRequestMappingAnnotation.class.getName();
|
||||
|
||||
private QuickfixRegistry registry;
|
||||
|
||||
public NoRequestMappingAnnotationReconciler(QuickfixRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector,
|
||||
boolean isCompleteAst) throws RequiredCompleteAstException {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
processAnnotation(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
processAnnotation(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
processAnnotation(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processAnnotation(Annotation a) {
|
||||
if (a.getParent() instanceof MethodDeclaration && isRequestMappingAnnotation(cu, a)) {
|
||||
String uri = docUri.toASCIIString();
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), LABEL, a.getStartPosition(), a.getLength());
|
||||
RewriteQuickFixUtils.setRewriteFixes(registry, problem, List.of(
|
||||
// new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.NODE))
|
||||
// .withRangeScope(RewriteQuickFixUtils.createOpenRewriteRange(cu, a))
|
||||
// .withRecipeScope(RecipeScope.NODE),
|
||||
new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE))
|
||||
.withRecipeScope(RecipeScope.FILE),
|
||||
new FixDescriptor(ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isRequestMappingAnnotation(CompilationUnit cu, Annotation a) {
|
||||
String typeName = a.getTypeName().getFullyQualifiedName();
|
||||
if (Annotations.SPRING_REQUEST_MAPPING.equals(typeName)) {
|
||||
return true;
|
||||
} else if (typeName.endsWith("RequestMapping")) {
|
||||
ITypeBinding type = a.resolveTypeBinding();
|
||||
if (type != null && Annotations.SPRING_REQUEST_MAPPING.equals(type.getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
return springBootVersionGreaterOrEqual(2, 0, 0).test(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProblemType getProblemType() {
|
||||
return Boot2JavaProblemType.JAVA_PRECISE_REQUEST_MAPPING;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.ReturnStatement;
|
||||
import org.openrewrite.java.spring.boot3.PreciseBeanType;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class PreciseBeanTypeReconciler implements JdtAstReconciler {
|
||||
|
||||
private static final String RECIPE_ID = PreciseBeanType.class.getName();
|
||||
|
||||
private static final String LABEL = "Ensure concrete bean type";
|
||||
|
||||
private QuickfixRegistry registry;
|
||||
|
||||
public PreciseBeanTypeReconciler(QuickfixRegistry registry) {
|
||||
this.registry = registry;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector,
|
||||
boolean isCompleteAst) throws RequiredCompleteAstException {
|
||||
AtomicBoolean requiresCompleteAst = new AtomicBoolean(false);
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
private MethodDeclaration currentMethod;
|
||||
|
||||
private List<ITypeBinding> currentReturnTypes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration method) {
|
||||
IMethodBinding methodBinding = method.resolveBinding();
|
||||
if (methodBinding != null) {
|
||||
boolean isBeanMethod = Arrays.stream(methodBinding.getAnnotations())
|
||||
.anyMatch(a -> AnnotationHierarchies.findTransitiveSuperAnnotationBindings(a).anyMatch(an -> Annotations.BEAN.equals(an.getAnnotationType().getQualifiedName())));
|
||||
if (isBeanMethod) {
|
||||
if (isCompleteAst) {
|
||||
if (currentMethod == null) {// Do not jump into anonymous class methods
|
||||
currentMethod = method;
|
||||
currentReturnTypes = new ArrayList<>();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
requiresCompleteAst.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(MethodDeclaration method) {
|
||||
if (currentMethod == method) {
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), LABEL, method.getReturnType2().getStartPosition(), method.getReturnType2().getLength());
|
||||
if (currentReturnTypes.size() > 1) {
|
||||
problemCollector.accept(problem);
|
||||
} else if (currentReturnTypes.size() == 1 && !method.resolveBinding().getReturnType().isAssignmentCompatible(currentReturnTypes.get(0))) {
|
||||
String uri = docUri.toASCIIString();
|
||||
String replacementType = currentReturnTypes.get(0).getName();
|
||||
RewriteQuickFixUtils.setRewriteFixes(registry, problem, List.of(
|
||||
new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel("Replace return type with '" + replacementType + "'", RecipeScope.NODE))
|
||||
.withRecipeScope(RecipeScope.NODE)
|
||||
.withRangeScope(RewriteQuickFixUtils.createOpenRewriteRange(cu, method)),
|
||||
new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE))
|
||||
.withRecipeScope(RecipeScope.FILE),
|
||||
new FixDescriptor(RECIPE_ID, List.of(uri), RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
.withRecipeScope(RecipeScope.PROJECT)
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
}
|
||||
|
||||
currentMethod = null;
|
||||
currentReturnTypes = new ArrayList<>();
|
||||
}
|
||||
super.endVisit(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ReturnStatement node) {
|
||||
ITypeBinding type = node.getExpression().resolveTypeBinding();
|
||||
if (currentReturnTypes.isEmpty()) {
|
||||
currentReturnTypes.add(type);
|
||||
} else {
|
||||
for (ListIterator<ITypeBinding> itr = currentReturnTypes.listIterator(); itr.hasNext();) {
|
||||
ITypeBinding t = itr.next();
|
||||
if (t.isAssignmentCompatible(type)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
currentReturnTypes.add(type);
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (requiresCompleteAst.get()) {
|
||||
throw new RequiredCompleteAstException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
return springBootVersionGreaterOrEqual(3, 0, 0).test(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProblemType getProblemType() {
|
||||
return SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,5 +89,22 @@ public class RewriteQuickFixUtils {
|
||||
for (; type != type.getErasure(); type = type.getErasure()) {}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static String getSimpleName(String fqName) {
|
||||
int idx = fqName.lastIndexOf('.');
|
||||
if (idx >= 0 && idx < fqName.length() - 1) {
|
||||
return fqName.substring(idx + 1);
|
||||
}
|
||||
return fqName;
|
||||
}
|
||||
|
||||
public static String getFirstTokenBeforeDot(String fqName) {
|
||||
int idx = fqName.indexOf('.');
|
||||
if (idx > 0) {
|
||||
return fqName.substring(0, idx);
|
||||
}
|
||||
return fqName;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.openrewrite.java.spring.boot2.UnnecessarySpringExtension;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class UnnecessarySpringExtensionReconciler implements JdtAstReconciler {
|
||||
|
||||
private static final String LABEL = "Remove unnecessary @SpringExtension";
|
||||
private static final String ID = UnnecessarySpringExtension.class.getName();
|
||||
private static final List<String> SPRING_BOOT_TEST_ANNOTATIONS = Arrays.asList(
|
||||
"org.springframework.boot.test.context.SpringBootTest",
|
||||
"org.springframework.boot.test.autoconfigure.jdbc.JdbcTest",
|
||||
"org.springframework.boot.test.autoconfigure.web.client.RestClientTest",
|
||||
"org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest",
|
||||
"org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest",
|
||||
"org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest",
|
||||
"org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest",
|
||||
"org.springframework.boot.test.autoconfigure.jooq.JooqTest",
|
||||
"org.springframework.boot.test.autoconfigure.json.JsonTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest",
|
||||
"org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest"
|
||||
);
|
||||
|
||||
private static final String FQN_EXTEND_WITH = "org.junit.jupiter.api.extension.ExtendWith";
|
||||
private static final String FQN_SPRING_EXT = "org.springframework.test.context.junit.jupiter.SpringExtension";
|
||||
|
||||
private QuickfixRegistry registry;
|
||||
|
||||
public UnnecessarySpringExtensionReconciler(QuickfixRegistry registry) {
|
||||
this.registry = registry;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconcile(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector,
|
||||
boolean isCompleteAst) throws RequiredCompleteAstException {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration typeDecl) {
|
||||
Annotation testAnnotation = null;
|
||||
Annotation extendWithAnnotation = null;
|
||||
for (Object o : typeDecl.modifiers()) {
|
||||
if (o instanceof Annotation) {
|
||||
Annotation a = (Annotation) o;
|
||||
if (testAnnotation == null && isApplicableTestAnnotation(a)) {
|
||||
testAnnotation = a;
|
||||
}
|
||||
if (extendWithAnnotation == null && isApplicableExtendsWith(a)) {
|
||||
extendWithAnnotation = a;
|
||||
}
|
||||
if (testAnnotation != null && extendWithAnnotation != null) {
|
||||
ReconcileProblemImpl problem = new ReconcileProblemImpl(getProblemType(), LABEL, extendWithAnnotation.getStartPosition(), extendWithAnnotation.getLength());
|
||||
RewriteQuickFixUtils.setRewriteFixes(registry, problem, List.of(
|
||||
new FixDescriptor(ID, List.of(docUri.toASCIIString()), RewriteQuickFixUtils.buildLabel(LABEL, RecipeScope.PROJECT))
|
||||
));
|
||||
problemCollector.accept(problem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(typeDecl);
|
||||
}
|
||||
|
||||
private boolean isApplicableExtendsWith(Annotation a) {
|
||||
if (FQN_EXTEND_WITH.endsWith(a.getTypeName().getFullyQualifiedName())) {
|
||||
IAnnotationBinding annotationBinding = a.resolveAnnotationBinding();
|
||||
if (annotationBinding != null && FQN_EXTEND_WITH.equals(annotationBinding.getAnnotationType().getQualifiedName()) && annotationBinding.getDeclaredMemberValuePairs().length == 1) {
|
||||
IMemberValuePairBinding pair = annotationBinding.getDeclaredMemberValuePairs()[0];
|
||||
if ("value".equals(pair.getName())) {
|
||||
ITypeBinding typeBinding = null;
|
||||
if (pair.getValue() instanceof ITypeBinding) {
|
||||
typeBinding = (ITypeBinding) pair.getValue();
|
||||
} else if (pair.getValue() instanceof Object[]) {
|
||||
Object[] arr = (Object[]) pair.getValue();
|
||||
if (arr.length > 0 && arr[0] instanceof ITypeBinding) {
|
||||
typeBinding = (ITypeBinding) arr[0];
|
||||
}
|
||||
}
|
||||
return typeBinding != null && FQN_SPRING_EXT.equals(typeBinding.getQualifiedName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isApplicableTestAnnotation(Annotation a) {
|
||||
String annotationTypeFqn = a.getTypeName().getFullyQualifiedName();
|
||||
if (SPRING_BOOT_TEST_ANNOTATIONS.stream().anyMatch(fqn -> fqn.endsWith(annotationTypeFqn))) {
|
||||
IAnnotationBinding annotationBinding = a.resolveAnnotationBinding();
|
||||
if (annotationBinding != null && SPRING_BOOT_TEST_ANNOTATIONS.contains(annotationBinding.getAnnotationType().getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(IJavaProject project) {
|
||||
return springBootVersionGreaterOrEqual(2, 1, 0).test(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boot2JavaProblemType getProblemType() {
|
||||
return Boot2JavaProblemType.JAVA_TEST_SPRING_EXTENSION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -643,10 +643,40 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
});
|
||||
|
||||
// reconciling
|
||||
IProblemCollector problemCollector = new IProblemCollector() {
|
||||
|
||||
List<ReconcileProblem> problems = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void endCollecting() {
|
||||
for (ReconcileProblem p : problems) {
|
||||
context.getProblemCollector().accept(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginCollecting() {
|
||||
problems.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(ReconcileProblem problem) {
|
||||
problems.add(problem);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
reconciler.reconcile(context.getProject(), URI.create(context.getDocURI()), context.getCu(), context.getProblemCollector(), context.getPass() == SCAN_PASS.TWO);
|
||||
problemCollector.beginCollecting();
|
||||
reconciler.reconcile(context.getProject(), URI.create(context.getDocURI()), context.getCu(), problemCollector, context.getPass() == SCAN_PASS.TWO);
|
||||
problemCollector.endCollecting();
|
||||
} catch (RequiredCompleteAstException e) {
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
if (context.getPass() == SCAN_PASS.TWO) {
|
||||
problemCollector.endCollecting();
|
||||
log.error("Complete AST required but it is complete already. Parsing ", context.getDocURI());
|
||||
} else {
|
||||
// Let problems be found in the next pass, don't add the problems to the aggregate problems collector to not duplicate them with the next pass
|
||||
context.getNextPassFiles().add(context.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
dependencyTracker.update(context.getFile(), context.getDependencies());;
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -40,8 +39,8 @@ import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaProjectReconcilerScheduler;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
@@ -66,10 +65,8 @@ public class ModulithService {
|
||||
private static final String CMD_LIST_MODULITH_PROJECTS = "sts/modulith/projects";
|
||||
|
||||
private SimpleLanguageServer server;
|
||||
private Optional<BootJavaProjectReconcilerScheduler> projectReconcileScheduler;
|
||||
private SpringSymbolIndex springIndex;
|
||||
private BootJavaReconcileEngine reconciler;
|
||||
private JavaProjectFinder projectFinder;
|
||||
private BootJavaConfig config;
|
||||
|
||||
private Map<URI, AppModules> cache;
|
||||
@@ -81,15 +78,12 @@ public class ModulithService {
|
||||
ProjectObserver projectObserver,
|
||||
SpringSymbolIndex springIndex,
|
||||
BootJavaReconcileEngine reconciler,
|
||||
Optional<BootJavaProjectReconcilerScheduler> projectReconcileScheduler,
|
||||
BootJavaConfig config
|
||||
) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.config = config;
|
||||
this.cache = new ConcurrentHashMap<>();
|
||||
this.metadataRequested = new ConcurrentHashMap<>();
|
||||
this.server = server;
|
||||
this.projectReconcileScheduler = projectReconcileScheduler;
|
||||
this.springIndex = springIndex;
|
||||
this.reconciler = reconciler;
|
||||
|
||||
@@ -209,12 +203,28 @@ public class ModulithService {
|
||||
private void validate(IJavaProject project) {
|
||||
if (server.getDiagnosticSeverityProvider().getDiagnosticSeverity(Boot3JavaProblemType.MODULITH_TYPE_REF_VIOLATION) != null
|
||||
&& config.getProblemApplicability(Boot3JavaProblemType.MODULITH_TYPE_REF_VIOLATION) != Option.OFF) {
|
||||
for (TextDocument doc : server.getTextDocumentService().getAll()) {
|
||||
if (projectFinder.find(doc.getId()).orElse(null) == project) {
|
||||
|
||||
List<Path> javaSources = IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath())
|
||||
.flatMap(sourceFolder -> {
|
||||
try {
|
||||
return Files.walk(sourceFolder.toPath()).filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".java"));
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
return Stream.empty();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
List<String> fileUriToUpdate = new ArrayList<>(javaSources.size());
|
||||
for (Path javaSource : javaSources) {
|
||||
String docUri = javaSource.toUri().toASCIIString();
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docUri);
|
||||
if (doc == null) {
|
||||
fileUriToUpdate.add(docUri);
|
||||
} else {
|
||||
server.validateWith(doc.getId(), reconciler);
|
||||
}
|
||||
}
|
||||
projectReconcileScheduler.ifPresent(r -> r.scheduleValidation(project));
|
||||
String[] uris = fileUriToUpdate.toArray(new String[fileUriToUpdate.size()]);
|
||||
springIndex.deleteDocuments(uris).thenAccept(v -> springIndex.updateDocuments(uris, "Modulith Metadata Changed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +314,7 @@ public class ModulithService {
|
||||
});
|
||||
}
|
||||
|
||||
static String getPackageNameFromTypeFQName(String fqn) {
|
||||
public static String getPackageNameFromTypeFQName(String fqn) {
|
||||
int idx = 0;
|
||||
for (; idx < fqn.length() - 1; idx++) {
|
||||
char c = fqn.charAt(idx);
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.NoRepoAnnotationReconciler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
|
||||
public class NoRepoAnnotationReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
@Override
|
||||
protected String getFolder() {
|
||||
return "norepoannotation";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProjectName() {
|
||||
return "test-spring-validations";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdtAstReconciler getReconciler() {
|
||||
return new NoRepoAnnotationReconciler(new QuickfixRegistry());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sanityTest() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
@org.springframework.stereotype.Repository
|
||||
interface A extends Repository {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(Boot2JavaProblemType.JAVA_REPOSITORY, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("@org.springframework.stereotype.Repository", markedStr);
|
||||
|
||||
assertEquals(2, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void inverseSanityTest() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
interface A extends org.springframework.data.repository.Repository {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(Boot2JavaProblemType.JAVA_REPOSITORY, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("@Repository", markedStr);
|
||||
|
||||
assertEquals(2, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyRepoAnnotation() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
@org.springframework.stereotype.Repository()
|
||||
interface A extends Repository {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(Boot2JavaProblemType.JAVA_REPOSITORY, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("@org.springframework.stereotype.Repository()", markedStr);
|
||||
|
||||
assertEquals(2, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.NoRequestMappingAnnotationReconciler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
|
||||
public class NoRequestMappingAnnotationReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
@Override
|
||||
protected String getFolder() {
|
||||
return "norequestmapping";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProjectName() {
|
||||
return "test-spring-validations";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdtAstReconciler getReconciler() {
|
||||
return new NoRequestMappingAnnotationReconciler(new QuickfixRegistry());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sanityTest() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
|
||||
@RequestMapping("/hello")
|
||||
class A {
|
||||
|
||||
@RequestMapping("/1")
|
||||
String hello1() {
|
||||
return "1";
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(Boot2JavaProblemType.JAVA_PRECISE_REQUEST_MAPPING, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("@RequestMapping(\"/1\")", markedStr);
|
||||
|
||||
assertEquals(2, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noProblems() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
|
||||
@RequestMapping("/hello")
|
||||
class A {
|
||||
|
||||
@Bean
|
||||
Integer someBean() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@GetMapping("/1")
|
||||
String hello1() {
|
||||
return "1";
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.java.SpringAotJavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.PreciseBeanTypeReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.RequiredCompleteAstException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class PreciseBeanTypeReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
@Override
|
||||
protected String getFolder() {
|
||||
return "precisebeantype";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProjectName() {
|
||||
return "test-spring-validations";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdtAstReconciler getReconciler() {
|
||||
return new PreciseBeanTypeReconciler(new QuickfixRegistry());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void requireFullAst() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
@Bean
|
||||
Number bean() {
|
||||
return Integer.valueOf(5);
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
try {
|
||||
reconcile("A.java", source, false);
|
||||
fail("Should require full AST with method bodies");
|
||||
} catch (RequiredCompleteAstException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleReturnStatement() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
@Bean
|
||||
Number bean() {
|
||||
return Integer.valueOf(5);
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Number", markedStr);
|
||||
|
||||
assertEquals(3, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleReturnStatementWithPrimitiveType() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
@Bean
|
||||
Number bean() {
|
||||
return 5;
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Number", markedStr);
|
||||
|
||||
assertEquals(3, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleReturnStatementSameType() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
boolean b
|
||||
|
||||
@Bean
|
||||
Number bean() {
|
||||
if (b) {
|
||||
return 3;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Number", markedStr);
|
||||
|
||||
assertEquals(3, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleReturnStatementDifferentType() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
boolean b
|
||||
|
||||
@Bean
|
||||
Number bean() {
|
||||
if (b) {
|
||||
return 3.45;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Number", markedStr);
|
||||
|
||||
assertEquals(0, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleReturnStatementDifferentType2() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import java.util.*;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
boolean b
|
||||
|
||||
@Bean
|
||||
Collection<Integer> bean() {
|
||||
if (b) {
|
||||
LinkedList<Integer> l = new LinkedList<>();
|
||||
return l;
|
||||
} else {
|
||||
return List.of(5);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Collection<Integer>", markedStr);
|
||||
|
||||
assertEquals(3, problem.getQuickfixes().size());
|
||||
|
||||
FixDescriptor d = (FixDescriptor) problem.getQuickfixes().get(0).params;
|
||||
|
||||
assertEquals("Replace return type with 'List<Integer>'", d.getLabel());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noProblemSingleReturnStatement() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
@Bean
|
||||
Integer bean() {
|
||||
return 5;
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleReturnStatement() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
boolean b
|
||||
|
||||
@Bean
|
||||
Double bean() {
|
||||
if (b) {
|
||||
return 3.45;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(SpringAotJavaProblemType.JAVA_CONCRETE_BEAN_TYPE, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("Double", markedStr);
|
||||
|
||||
assertEquals(0, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noProblemMultipleReturnStatement() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
class A {
|
||||
|
||||
boolean b
|
||||
|
||||
@Bean
|
||||
Double bean() {
|
||||
if (b) {
|
||||
return 3.45;
|
||||
} else {
|
||||
return 5.4;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* VMware, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.UnnecessarySpringExtensionReconciler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
|
||||
public class UnnecessarySpringExtensionReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
@Override
|
||||
protected String getFolder() {
|
||||
// TODO Auto-generated method stub
|
||||
return "unnecassaryextendwith";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProjectName() {
|
||||
return "test-spring-validations";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdtAstReconciler getReconciler() {
|
||||
return new UnnecessarySpringExtensionReconciler(new QuickfixRegistry());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sanity() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest
|
||||
class A {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
ReconcileProblem problem = problems.get(0);
|
||||
|
||||
assertEquals(Boot2JavaProblemType.JAVA_TEST_SPRING_EXTENSION, problem.getType());
|
||||
|
||||
String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength());
|
||||
assertEquals("@ExtendWith(SpringExtension.class)", markedStr);
|
||||
|
||||
assertEquals(1, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTestAnnotation() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
class A {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void noExtendWithAnnotation() throws Exception {
|
||||
String source = """
|
||||
package example.demo;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class A {
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, true);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,9 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.AnnotationNodeReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
|
||||
@@ -148,7 +150,9 @@ public class ValueSpelExpressionValidationTest {
|
||||
|
||||
problemCollector = new TestProblemCollector();
|
||||
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
|
||||
new JdtReconciler(compilationUnitCache, null, config)
|
||||
new JdtReconciler(compilationUnitCache, config, new JdtAstReconciler[] {
|
||||
new AnnotationNodeReconciler(config)
|
||||
})
|
||||
}, server);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user