diff --git a/src/main/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolation.java b/src/main/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolation.java new file mode 100644 index 0000000..bc3274d --- /dev/null +++ b/src/main/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolation.java @@ -0,0 +1,34 @@ +package org.greencodeinitiative.creedengo.python.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.python.api.PythonSubscriptionCheck; +import org.sonar.plugins.python.api.SubscriptionContext; +import org.sonar.plugins.python.api.tree.*; +import java.util.regex.Pattern; + +@Rule(key = "GCI1066") +public class DetectBadLoggingFormatInterpolation extends PythonSubscriptionCheck { + + protected static final String MESSAGE_RULE = "For logging format, prefer using %s with kwargs instead of builtin formatter \"\".format() or f\"\""; + private static final Pattern PATTERN_FORMAT = Pattern.compile("f['\"].*['\"]"); + private static final Pattern PATTERN_LOGGER = Pattern.compile("logging|logger"); + private static final Pattern PATTERN_LOGGING_METHOD = Pattern.compile("info|debug|error"); + + @Override + public void initialize(Context context) { + context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::visitNodeString); + } + + public void visitNodeString(SubscriptionContext ctx) { + CallExpression callExpression = (CallExpression) ctx.syntaxNode(); + + QualifiedExpression qualifiedExpression = (QualifiedExpression)((CallExpression)ctx.syntaxNode()).callee(); + + if(PATTERN_LOGGING_METHOD.matcher(qualifiedExpression.name().name()).matches() + && PATTERN_LOGGER.matcher(((Name)qualifiedExpression.qualifier()).name()).matches() + && PATTERN_FORMAT.matcher(((RegularArgument)callExpression.arguments().get(0)).expression().firstToken().value()).matches()) { + ctx.addIssue(callExpression, MESSAGE_RULE); + } + } + +} diff --git a/src/test/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolationTest.java b/src/test/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolationTest.java new file mode 100644 index 0000000..3e134fd --- /dev/null +++ b/src/test/java/org/greencodeinitiative/creedengo/python/checks/DetectBadLoggingFormatInterpolationTest.java @@ -0,0 +1,14 @@ +package org.greencodeinitiative.creedengo.python.checks; + +import org.junit.Test; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +public class DetectBadLoggingFormatInterpolationTest { + + @Test + public void test_bad_logging_format_interpolation() { + PythonCheckVerifier.verify("src/test/resources/checks/detectBadLoggingFormatInterpolationNonCompliant.py", new DetectBadLoggingFormatInterpolation()); + PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/detectBadLoggingFormatInterpolationCompliant.py", new DetectBadLoggingFormatInterpolation()); + } + +} diff --git a/src/test/resources/checks/detectBadLoggingFormatInterpolationCompliant.py b/src/test/resources/checks/detectBadLoggingFormatInterpolationCompliant.py new file mode 100644 index 0000000..f1f2303 --- /dev/null +++ b/src/test/resources/checks/detectBadLoggingFormatInterpolationCompliant.py @@ -0,0 +1,6 @@ +import logging +logger = logging.getLogger() +name = "world" + +logging.info("Hello %s", name) # Correct +logger.debug("Hello %s", name) # Correct \ No newline at end of file diff --git a/src/test/resources/checks/detectBadLoggingFormatInterpolationNonCompliant.py b/src/test/resources/checks/detectBadLoggingFormatInterpolationNonCompliant.py new file mode 100644 index 0000000..0b162ea --- /dev/null +++ b/src/test/resources/checks/detectBadLoggingFormatInterpolationNonCompliant.py @@ -0,0 +1,7 @@ +import logging +logger = logging.getLogger() +name = "world" + +#logging.info("Hello {}".format(name)) # Noncompliant {{For logging format, prefer using %s with kwargs instead of builtin formatter "".format() or f""}} +logger.debug(f'Hello {name}') # Noncompliant {{For logging format, prefer using %s with kwargs instead of builtin formatter "".format() or f""}} +logger.error(f"Hello {name}") # Noncompliant {{For logging format, prefer using %s with kwargs instead of builtin formatter "".format() or f""}} \ No newline at end of file