Skip to content

Commit

Permalink
feat(ec528): ported rule ec528 to swift
Browse files Browse the repository at this point in the history
  • Loading branch information
rgoussu-exalt committed May 30, 2024
1 parent e7cb7c5 commit ffcd2a1
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* ecoCode iOS plugin - Help the earth, adopt this green plugin for your applications
* Copyright © 2023 green-code-initiative (https://www.ecocode.io/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.ecocode.ios.swift.checks.sobriety;

import io.ecocode.ios.swift.EcoCodeSwiftVisitor;
import io.ecocode.ios.swift.SwiftRuleCheck;
import io.ecocode.ios.swift.antlr.generated.Swift5Parser;
import io.ecocode.ios.swift.checks.CheckHelper;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.check.Rule;

import java.util.Objects;

@Rule(key = "EC528")
public class FeedbackGeneratorUsageCheck extends SwiftRuleCheck {
private static final Logger LOGGER = Loggers.get(EcoCodeSwiftVisitor.class);
private static final String DEFAULT_ISSUE_MESSAGE = "Avoid using the device vibrator to use less energy.";
public static final String UI_KIT = "UIKit";
protected boolean isUIKitImported;

protected Swift5Parser.ExpressionContext id;

protected boolean isFeedbackGeneratorInstantiated;
protected boolean isImpactMethodCalled;

@Override
public void apply(ParseTree tree) {

isUIKitImported = isUIKitImported || CheckHelper.isImportExisting(tree, UI_KIT);

isFeedbackGeneratorInstantiated = isFeedbackGeneratorInstantiated || (isUIKitImported &&
tree instanceof Swift5Parser.ExpressionContext &&
(tree.getText().contains("UIImpactFeedbackGenerator")));

isImpactMethodCalled = isImpactMethodCalled || (isFeedbackGeneratorInstantiated &&
(tree.getText().contains(".impactOccurred(")));

if (Objects.isNull(id) &&
tree instanceof Swift5Parser.ExpressionContext &&
isImpactMethodCalled
) {
id = (Swift5Parser.ExpressionContext) tree;
}

if (tree instanceof TerminalNodeImpl && tree.getText().equals("<EOF>")) {
if (isImpactMethodCalled) {
this.recordIssue(id.getStart().getStartIndex(), DEFAULT_ISSUE_MESSAGE);
}
isUIKitImported = false;
isFeedbackGeneratorInstantiated = false;
isImpactMethodCalled = false;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public class LocationLeakCheck extends SwiftRuleCheck {
@Override
public void apply(ParseTree tree) {


if (tree instanceof Swift5Parser.ExpressionContext && (tree.getText().contains(".startUpdatingLocation()"))) {
firstCallExist = true;
id = (Swift5Parser.ExpressionContext) tree;
Expand Down
1 change: 1 addition & 0 deletions swift-lang/src/main/resources/ecocode_swift_profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"EC520",
"EC522",
"EC524",
"EC528",
"EC530",
"EC533",
"EC534",
Expand Down
14 changes: 14 additions & 0 deletions swift-lang/src/main/resources/io/ecocode/rules/swift/EC528.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<p>
Shaking or vibrating an iOS device is possible using <code>UIFeedbackGenerator.</code>
Behind this effect stands a specific hardware component, the Taptic Engine, which consumes power.
As a consequence, its usage should be carefully considered, especially if its added value is not clear.
<h2>Noncompliant Code Example</h2>

<pre>
import UIKit

func triggerVibration() {
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
}
</pre>
18 changes: 18 additions & 0 deletions swift-lang/src/main/resources/io/ecocode/rules/swift/EC528.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"key": "EC528",
"title": "Sobriety: Vibration Free",
"defaultSeverity": "Major",
"description": "Avoid using the device vibrator to use less energy.",
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"constantCost": "5min"
},
"tags": [
"ecocode",
"environment",
"sobriety",
"eco-design"
],
"type": "CODE_SMELL"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* ecoCode iOS plugin - Help the earth, adopt this green plugin for your applications
* Copyright © 2023 green-code-initiative (https://www.ecocode.io/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package io.ecocode.ios.swift.checks.sobriety;

import io.ecocode.ios.swift.checks.CheckTestHelper;
import org.assertj.core.api.ObjectAssert;
import org.junit.Test;
import org.sonar.api.batch.fs.TextPointer;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueLocation;
import org.sonar.api.rule.RuleKey;

import static org.assertj.core.api.Assertions.assertThat;

public class FeedbackGeneratorUsageCheckTest {

private static final String TEST_CASE_FEEDBACK_GENERATOR_USE = "checks/sobriety/FeedbackGeneratorUsageCheck_abusive_use_trigger.swift";
private static final String TEST_CASE_COMPLIANT = "checks/sobriety/FeedbackGeneratorUsage_compliant_no_trigger.swift";
@Test
public void feedbackGeneratorUsageCheck_usage_trigger(){
SensorContextTester context = CheckTestHelper.analyzeTestFile(TEST_CASE_FEEDBACK_GENERATOR_USE);
ObjectAssert<Issue> issue = assertThat(context.allIssues()).hasSize(1)
.first();
issue.extracting(Issue::ruleKey).extracting(RuleKey::rule).isEqualTo("EC528");
issue.extracting(Issue::ruleKey).extracting(RuleKey::repository)
.isEqualTo("ecoCode-swift");
issue.extracting(Issue::primaryLocation)
.extracting(IssueLocation::textRange)
.extracting(TextRange::start)
.extracting(TextPointer::line)
.isEqualTo(5);
}

@Test
public void locationLeakCheck_compliant_no_trigger(){
SensorContextTester context = CheckTestHelper.analyzeTestFile(TEST_CASE_COMPLIANT);
assertThat(context.allIssues()).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import UIKit

func triggerVibration() {
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UIKit

func hello() {
print("Hello, world!")
}

0 comments on commit ffcd2a1

Please sign in to comment.