Skip to content

Commit b4f1237

Browse files
authored
Sonar codemod to remove redundant variable creation expression (#253)
Remove redundant variable creation expression when it is only returned or thrown [Given examples of the file](https://sonarcloud.io/project/issues?fileUuids=AYvtrjqJLCzGLicz7AcH&resolved=false&types=CODE_SMELL&id=nahsra_WebGoat_10_23): [Immediately return this expression instead of assigning it to the temporary variable "localeResolver".](https://sonarcloud.io/project/issues?open=AYvtrj5QLCzGLicz7AqQ&id=nahsra_WebGoat_10_23) [Immediately throw this expression instead of assigning it to the temporary variable "myException".](https://sonarcloud.io/project/issues?open=AYw20oU9yE-xQ1mlDtlZ&id=nahsra_WebGoat_10_23) Sonar rule reference: https://rules.sonarsource.com/java/RSPEC-1488/
1 parent c9fa81e commit b4f1237

File tree

7 files changed

+743
-0
lines changed

7 files changed

+743
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.codemodder.codemods;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import com.github.javaparser.ast.expr.*;
5+
import com.github.javaparser.ast.stmt.*;
6+
import io.codemodder.*;
7+
import io.codemodder.providers.sonar.ProvidedSonarScan;
8+
import io.codemodder.providers.sonar.RuleIssues;
9+
import io.codemodder.providers.sonar.SonarPluginJavaParserChanger;
10+
import io.codemodder.providers.sonar.api.Issue;
11+
import java.util.Optional;
12+
import javax.inject.Inject;
13+
14+
/** A codemod to remove redundant variable creation */
15+
@Codemod(
16+
id = "sonar:java/remove-redundant-variable-creation-s1488",
17+
reviewGuidance = ReviewGuidance.MERGE_WITHOUT_REVIEW,
18+
executionPriority = CodemodExecutionPriority.HIGH)
19+
public final class RemoveRedundantVariableCreationCodemod
20+
extends SonarPluginJavaParserChanger<ObjectCreationExpr> {
21+
22+
@Inject
23+
public RemoveRedundantVariableCreationCodemod(
24+
@ProvidedSonarScan(ruleId = "java:S1488") final RuleIssues issues) {
25+
super(issues, ObjectCreationExpr.class, RegionNodeMatcher.MATCHES_START);
26+
}
27+
28+
@Override
29+
public boolean onIssueFound(
30+
final CodemodInvocationContext context,
31+
final CompilationUnit cu,
32+
final ObjectCreationExpr objectCreationExpr,
33+
final Issue issue) {
34+
35+
// Get full block statement
36+
final Optional<BlockStmt> blockStmtOpt = objectCreationExpr.findAncestor(BlockStmt.class);
37+
if (blockStmtOpt.isPresent()) {
38+
final BlockStmt blockStmt = blockStmtOpt.get();
39+
40+
// Retrieve return/throw statement to update its expression to given objectCreationExpr param
41+
final Optional<Statement> lastStmtOpt = blockStmt.getStatements().getLast();
42+
43+
// Retrieve the redundant variable declaration expression that will be removed
44+
final Optional<ExpressionStmt> exprStmtOpt =
45+
objectCreationExpr.findAncestor(ExpressionStmt.class);
46+
47+
if (lastStmtOpt.isPresent() && exprStmtOpt.isPresent()) {
48+
final Statement lastStmt = lastStmtOpt.get();
49+
if (lastStmt instanceof ReturnStmt returnStmt) {
50+
returnStmt.setExpression(objectCreationExpr);
51+
} else if (lastStmt instanceof ThrowStmt throwStmt) {
52+
throwStmt.setExpression(objectCreationExpr);
53+
} else {
54+
return false;
55+
}
56+
57+
// Remove the redundant variable creation expression
58+
blockStmt.getStatements().remove(exprStmtOpt.get());
59+
60+
return true;
61+
}
62+
}
63+
64+
return false;
65+
}
66+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
This change removes intermediate variables who are only created to be thrown or returned in the next statement. This makes the code more readable, which makes reviewing the code for issues easier.
2+
3+
Our changes look something like this:
4+
5+
```diff
6+
public LocaleResolver localeResolver() {
7+
- SessionLocaleResolver localeResolver = new SessionLocaleResolver();
8+
- return localeResolver;
9+
+ return new SessionLocaleResolver();
10+
}
11+
```
12+
13+
```diff
14+
public void process() {
15+
- Exception ex = new Exception();
16+
- throw ex;
17+
+ throw new Exception();
18+
}
19+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"summary" : "Remove redundant variable creation expression when it is only returned/thrown (Sonar).",
3+
"change" : "Remove redundant variable creation expression when it is only returned/thrown.",
4+
"references" : [
5+
"https://rules.sonarsource.com/java/RSPEC-1488/"
6+
]
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.codemodder.codemods;
2+
3+
import io.codemodder.testutils.CodemodTestMixin;
4+
import io.codemodder.testutils.Metadata;
5+
6+
@Metadata(
7+
codemodType = RemoveRedundantVariableCreationCodemod.class,
8+
testResourceDir = "remove-redundant-variable-creation-s1488",
9+
renameTestFile = "src/main/java/org/owasp/webgoat/container/MvcConfiguration.java",
10+
dependencies = {})
11+
final class RemoveRedundantVariableCreationCodemodTest implements CodemodTestMixin {}
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/**
2+
* ************************************************************************************************
3+
*
4+
* <p>
5+
*
6+
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
7+
* please see http://www.owasp.org/
8+
*
9+
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
10+
*
11+
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
12+
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
13+
* License, or (at your option) any later version.
14+
*
15+
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
16+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* <p>You should have received a copy of the GNU General Public License along with this program; if
20+
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21+
* 02111-1307, USA.
22+
*
23+
* <p>Getting Source ==============
24+
*
25+
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
26+
* for free software projects.
27+
*
28+
* @author WebGoat
29+
* @version $Id: $Id
30+
* @since October 28, 2003
31+
*/
32+
package org.owasp.webgoat.container;
33+
34+
import java.io.IOException;
35+
import java.nio.charset.StandardCharsets;
36+
import java.util.Map;
37+
import java.util.Set;
38+
import lombok.RequiredArgsConstructor;
39+
import lombok.extern.slf4j.Slf4j;
40+
import org.owasp.webgoat.container.i18n.Language;
41+
import org.owasp.webgoat.container.i18n.Messages;
42+
import org.owasp.webgoat.container.i18n.PluginMessages;
43+
import org.owasp.webgoat.container.lessons.LessonScanner;
44+
import org.owasp.webgoat.container.session.LabelDebugger;
45+
import org.springframework.context.ApplicationContext;
46+
import org.springframework.context.annotation.Bean;
47+
import org.springframework.context.annotation.Configuration;
48+
import org.springframework.core.io.ResourceLoader;
49+
import org.springframework.core.io.support.ResourcePatternResolver;
50+
import org.springframework.web.servlet.LocaleResolver;
51+
import org.springframework.web.servlet.ViewResolver;
52+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
53+
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
54+
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
55+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
56+
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
57+
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
58+
import org.thymeleaf.IEngineConfiguration;
59+
import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect;
60+
import org.thymeleaf.spring6.SpringTemplateEngine;
61+
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
62+
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
63+
import org.thymeleaf.templatemode.TemplateMode;
64+
import org.thymeleaf.templateresolver.FileTemplateResolver;
65+
import org.thymeleaf.templateresolver.ITemplateResolver;
66+
import org.thymeleaf.templateresource.ITemplateResource;
67+
import org.thymeleaf.templateresource.StringTemplateResource;
68+
69+
/** Configuration for Spring MVC */
70+
@Configuration
71+
@RequiredArgsConstructor
72+
@Slf4j
73+
public class MvcConfiguration implements WebMvcConfigurer {
74+
75+
private static final String UTF8 = "UTF-8";
76+
77+
private final LessonScanner lessonScanner;
78+
79+
@Override
80+
public void addViewControllers(ViewControllerRegistry registry) {
81+
registry.addViewController("/login").setViewName("login");
82+
registry.addViewController("/lesson_content").setViewName("lesson_content");
83+
registry.addViewController("/start.mvc").setViewName("main_new");
84+
registry.addViewController("/scoreboard").setViewName("scoreboard");
85+
}
86+
87+
@Bean
88+
public ViewResolver viewResolver(SpringTemplateEngine thymeleafTemplateEngine) {
89+
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
90+
resolver.setTemplateEngine(thymeleafTemplateEngine);
91+
resolver.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
92+
return resolver;
93+
}
94+
95+
/**
96+
* Responsible for loading lesson templates based on Thymeleaf, for example:
97+
*
98+
* <p><div th:include="/lessons/spoofcookie/templates/spoofcookieform.html" id="content"></div>
99+
*/
100+
@Bean
101+
public ITemplateResolver lessonThymeleafTemplateResolver(ResourceLoader resourceLoader) {
102+
var resolver =
103+
new FileTemplateResolver() {
104+
@Override
105+
protected ITemplateResource computeTemplateResource(
106+
IEngineConfiguration configuration,
107+
String ownerTemplate,
108+
String template,
109+
String resourceName,
110+
String characterEncoding,
111+
Map<String, Object> templateResolutionAttributes) {
112+
try (var is =
113+
resourceLoader.getResource("classpath:" + resourceName).getInputStream()) {
114+
return new StringTemplateResource(
115+
new String(is.readAllBytes(), StandardCharsets.UTF_8));
116+
} catch (IOException e) {
117+
return null;
118+
}
119+
}
120+
};
121+
resolver.setOrder(1);
122+
return resolver;
123+
}
124+
125+
/** Loads all normal WebGoat specific Thymeleaf templates */
126+
@Bean
127+
public ITemplateResolver springThymeleafTemplateResolver(ApplicationContext applicationContext) {
128+
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
129+
resolver.setPrefix("classpath:/webgoat/templates/");
130+
resolver.setSuffix(".html");
131+
resolver.setTemplateMode(TemplateMode.HTML);
132+
resolver.setOrder(2);
133+
resolver.setCharacterEncoding(UTF8);
134+
resolver.setApplicationContext(applicationContext);
135+
return resolver;
136+
}
137+
138+
/** Loads the html for the complete lesson, see lesson_content.html */
139+
@Bean
140+
public LessonTemplateResolver lessonTemplateResolver(ResourceLoader resourceLoader) {
141+
LessonTemplateResolver resolver = new LessonTemplateResolver(resourceLoader);
142+
resolver.setOrder(0);
143+
resolver.setCacheable(false);
144+
resolver.setCharacterEncoding(UTF8);
145+
return resolver;
146+
}
147+
148+
/** Loads the lesson asciidoc. */
149+
@Bean
150+
public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver(
151+
Language language, ResourceLoader resourceLoader) {
152+
log.debug("template locale {}", language);
153+
AsciiDoctorTemplateResolver resolver =
154+
new AsciiDoctorTemplateResolver(language, resourceLoader);
155+
resolver.setCacheable(false);
156+
resolver.setOrder(1);
157+
resolver.setCharacterEncoding(UTF8);
158+
return resolver;
159+
}
160+
161+
@Bean
162+
public SpringTemplateEngine thymeleafTemplateEngine(
163+
ITemplateResolver springThymeleafTemplateResolver,
164+
LessonTemplateResolver lessonTemplateResolver,
165+
AsciiDoctorTemplateResolver asciiDoctorTemplateResolver,
166+
ITemplateResolver lessonThymeleafTemplateResolver) {
167+
SpringTemplateEngine engine = new SpringTemplateEngine();
168+
engine.setEnableSpringELCompiler(true);
169+
engine.addDialect(new SpringSecurityDialect());
170+
engine.setTemplateResolvers(
171+
Set.of(
172+
lessonTemplateResolver,
173+
asciiDoctorTemplateResolver,
174+
lessonThymeleafTemplateResolver,
175+
springThymeleafTemplateResolver));
176+
return engine;
177+
}
178+
179+
@Override
180+
public void addResourceHandlers(ResourceHandlerRegistry registry) {
181+
// WebGoat internal
182+
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/webgoat/static/css/");
183+
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/webgoat/static/js/");
184+
registry
185+
.addResourceHandler("/plugins/**")
186+
.addResourceLocations("classpath:/webgoat/static/plugins/");
187+
registry
188+
.addResourceHandler("/fonts/**")
189+
.addResourceLocations("classpath:/webgoat/static/fonts/");
190+
191+
// WebGoat lessons
192+
registry
193+
.addResourceHandler("/images/**")
194+
.addResourceLocations(
195+
lessonScanner.applyPattern("classpath:/lessons/%s/images/").toArray(String[]::new));
196+
registry
197+
.addResourceHandler("/lesson_js/**")
198+
.addResourceLocations(
199+
lessonScanner.applyPattern("classpath:/lessons/%s/js/").toArray(String[]::new));
200+
registry
201+
.addResourceHandler("/lesson_css/**")
202+
.addResourceLocations(
203+
lessonScanner.applyPattern("classpath:/lessons/%s/css/").toArray(String[]::new));
204+
registry
205+
.addResourceHandler("/lesson_templates/**")
206+
.addResourceLocations(
207+
lessonScanner.applyPattern("classpath:/lessons/%s/templates/").toArray(String[]::new));
208+
registry
209+
.addResourceHandler("/video/**")
210+
.addResourceLocations(
211+
lessonScanner.applyPattern("classpath:/lessons/%s/video/").toArray(String[]::new));
212+
}
213+
214+
@Bean
215+
public PluginMessages pluginMessages(
216+
Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) {
217+
PluginMessages pluginMessages = new PluginMessages(messages, language, resourcePatternResolver);
218+
pluginMessages.setDefaultEncoding("UTF-8");
219+
pluginMessages.setBasenames("i18n/WebGoatLabels");
220+
pluginMessages.setFallbackToSystemLocale(false);
221+
return pluginMessages;
222+
}
223+
224+
@Bean
225+
public Language language(LocaleResolver localeResolver) {
226+
return new Language(localeResolver);
227+
}
228+
229+
@Bean
230+
public LocaleResolver localeResolver() {
231+
return new SessionLocaleResolver();
232+
}
233+
234+
public void doSomething() {
235+
throw new RuntimeException();
236+
}
237+
238+
@Bean
239+
public LocaleChangeInterceptor localeChangeInterceptor() {
240+
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
241+
lci.setParamName("lang");
242+
return lci;
243+
}
244+
245+
@Override
246+
public void addInterceptors(InterceptorRegistry registry) {
247+
registry.addInterceptor(localeChangeInterceptor());
248+
}
249+
250+
@Bean
251+
public Messages messageSource(Language language) {
252+
Messages messages = new Messages(language);
253+
messages.setDefaultEncoding("UTF-8");
254+
messages.setBasename("classpath:i18n/messages");
255+
messages.setFallbackToSystemLocale(false);
256+
return messages;
257+
}
258+
259+
@Bean
260+
public LabelDebugger labelDebugger() {
261+
return new LabelDebugger();
262+
}
263+
}

0 commit comments

Comments
 (0)