diff --git a/python-checks/src/main/java/org/sonar/python/checks/AbstractCallExpressionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/AbstractCallExpressionCheck.java index 41eea95b0a..adf8502ce9 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/AbstractCallExpressionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/AbstractCallExpressionCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Collections; import java.util.Set; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.semantic.Symbol; -public abstract class AbstractCallExpressionCheck extends PythonCheck { +public abstract class AbstractCallExpressionCheck extends PythonCheckAstNode { protected abstract Set functionsToCheck(); diff --git a/python-checks/src/main/java/org/sonar/python/checks/AbstractNameCheck.java b/python-checks/src/main/java/org/sonar/python/checks/AbstractNameCheck.java index a71bec2424..01ac328619 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/AbstractNameCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/AbstractNameCheck.java @@ -20,9 +20,9 @@ package org.sonar.python.checks; import java.util.regex.Pattern; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; -public abstract class AbstractNameCheck extends PythonCheck { +public abstract class AbstractNameCheck extends PythonCheckAstNode { private Pattern pattern = null; diff --git a/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java b/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java index 6abae37ed1..4b4005d00f 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/AfterJumpStatementCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = AfterJumpStatementCheck.CHECK_KEY) -public class AfterJumpStatementCheck extends PythonCheck { +public class AfterJumpStatementCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1763"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/BackslashInStringCheck.java b/python-checks/src/main/java/org/sonar/python/checks/BackslashInStringCheck.java index 2443ca458b..a204ba5a4f 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/BackslashInStringCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/BackslashInStringCheck.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonTokenType; @Rule(key = "S1717") -public class BackslashInStringCheck extends PythonCheck { +public class BackslashInStringCheck extends PythonCheckAstNode { private static final String MESSAGE = "Remove this \"\\\", add another \"\\\" to escape it, or make this a raw string."; private static final String VALID_ESCAPED_CHARACTERS = "abfnrtvxnNrtuU\\'\"0123456789\n\r"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/BackticksUsageCheck.java b/python-checks/src/main/java/org/sonar/python/checks/BackticksUsageCheck.java index 2a1a918a71..dec42aab98 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/BackticksUsageCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/BackticksUsageCheck.java @@ -24,12 +24,12 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; @Rule(key = BackticksUsageCheck.CHECK_KEY) -public class BackticksUsageCheck extends PythonCheck { +public class BackticksUsageCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "BackticksUsage"; @Override diff --git a/python-checks/src/main/java/org/sonar/python/checks/BreakContinueOutsideLoopCheck.java b/python-checks/src/main/java/org/sonar/python/checks/BreakContinueOutsideLoopCheck.java index 398f7da19d..91f86e5514 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/BreakContinueOutsideLoopCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/BreakContinueOutsideLoopCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = BreakContinueOutsideLoopCheck.CHECK_KEY) -public class BreakContinueOutsideLoopCheck extends PythonCheck { +public class BreakContinueOutsideLoopCheck extends PythonCheckAstNode { private static final String MESSAGE = "Remove this \"%s\" statement"; public static final String CHECK_KEY = "S1716"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/CheckList.java b/python-checks/src/main/java/org/sonar/python/checks/CheckList.java index df5a4670a5..9a6b659921 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/CheckList.java +++ b/python-checks/src/main/java/org/sonar/python/checks/CheckList.java @@ -19,7 +19,9 @@ */ package org.sonar.python.checks; -import org.sonar.python.PythonCheck; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import org.sonar.python.checks.hotspots.CommandLineArgsCheck; import org.sonar.python.checks.hotspots.DebugModeCheck; import org.sonar.python.checks.hotspots.DynamicCodeExecutionCheck; @@ -38,7 +40,7 @@ private CheckList() { } public static Iterable getChecks() { - return PythonCheck.immutableSet( + return Collections.unmodifiableSet(new HashSet<>(Arrays.asList( AfterJumpStatementCheck.class, BackslashInStringCheck.class, BackticksUsageCheck.class, @@ -102,7 +104,7 @@ public static Iterable getChecks() { UselessParenthesisAfterKeywordCheck.class, UselessParenthesisCheck.class, XPathCheck.class - ); + ))); } } diff --git a/python-checks/src/main/java/org/sonar/python/checks/ClassComplexityCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ClassComplexityCheck.java index ce0c3bc150..e3eaa9a64c 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ClassComplexityCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ClassComplexityCheck.java @@ -25,12 +25,12 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.metrics.ComplexityVisitor; @Rule(key = "ClassComplexity") -public class ClassComplexityCheck extends PythonCheck { +public class ClassComplexityCheck extends PythonCheckAstNode { private static final int DEFAULT_MAXIMUM_CLASS_COMPLEXITY_THRESHOLD = 200; private static final String MESSAGE = "Class has a complexity of %s which is greater than %s authorized."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/CognitiveComplexityFunctionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/CognitiveComplexityFunctionCheck.java index 31163e8ddf..255085b5cc 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/CognitiveComplexityFunctionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/CognitiveComplexityFunctionCheck.java @@ -27,12 +27,12 @@ import org.sonar.check.Rule; import org.sonar.check.RuleProperty; import org.sonar.python.IssueLocation; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.metrics.CognitiveComplexityVisitor; @Rule(key = CognitiveComplexityFunctionCheck.CHECK_KEY) -public class CognitiveComplexityFunctionCheck extends PythonCheck { +public class CognitiveComplexityFunctionCheck extends PythonCheckAstNode { private static final String MESSAGE = "Refactor this function to reduce its Cognitive Complexity from %s to the %s allowed."; public static final String CHECK_KEY = "S3776"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/CollapsibleIfStatementsCheck.java b/python-checks/src/main/java/org/sonar/python/checks/CollapsibleIfStatementsCheck.java index 5dcc4fbca6..55cd74d77d 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/CollapsibleIfStatementsCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/CollapsibleIfStatementsCheck.java @@ -19,54 +19,50 @@ */ package org.sonar.python.checks; -import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.AstNodeType; -import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; -import org.sonar.python.api.PythonGrammar; -import org.sonar.python.api.PythonKeyword; -import org.sonar.sslr.ast.AstSelect; +import org.sonar.python.PythonCheckTree; +import org.sonar.python.PythonVisitorContext; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.Tree; @Rule(key = CollapsibleIfStatementsCheck.CHECK_KEY) -public class CollapsibleIfStatementsCheck extends PythonCheck { +public class CollapsibleIfStatementsCheck extends PythonCheckTree { public static final String CHECK_KEY = "S1066"; private static final String MESSAGE = "Merge this if statement with the enclosing one."; + private Set ignored = new HashSet<>(); + @Override - public Set subscribedKinds() { - return Collections.singleton(PythonGrammar.IF_STMT); + public void scanFile(PythonVisitorContext visitorContext) { + ignored.clear(); + super.scanFile(visitorContext); } @Override - public void visitNode(AstNode node) { - AstNode suite = node.getLastChild(PythonGrammar.SUITE); - if (suite.getPreviousSibling().getPreviousSibling().is(PythonKeyword.ELSE)) { - return; - } - AstNode singleIfChild = singleIfChild(suite); - if (singleIfChild != null && !hasElseOrElif(singleIfChild)) { - addIssue(singleIfChild.getToken(), MESSAGE) - .secondary(node.getFirstChild(), "enclosing"); + public void visitIfStatement(PyIfStatementTree ifStatement) { + List statements = ifStatement.body(); + if (!ifStatement.elifBranches().isEmpty()) { + if (ifStatement.elseBranch() == null) { + ignored.addAll(ifStatement.elifBranches().subList(0, ifStatement.elifBranches().size() - 1)); + } else { + ignored.addAll(ifStatement.elifBranches()); + } } - } - - private static boolean hasElseOrElif(AstNode ifNode) { - return ifNode.hasDirectChildren(PythonKeyword.ELIF) || ifNode.hasDirectChildren(PythonKeyword.ELSE); - } - - private static AstNode singleIfChild(AstNode suite) { - List statements = suite.getChildren(PythonGrammar.STATEMENT); - if (statements.size() == 1) { - AstSelect nestedIf = statements.get(0).select() - .children(PythonGrammar.COMPOUND_STMT) - .children(PythonGrammar.IF_STMT); - if (nestedIf.size() == 1) { - return nestedIf.get(0); + if (!ignored.contains(ifStatement) + && ifStatement.elseBranch() == null + && ifStatement.elifBranches().isEmpty() + && statements.size() == 1 + && statements.get(0).is(Tree.Kind.IF_STMT)) { + PyIfStatementTree singleIfChild = (PyIfStatementTree) statements.get(0); + if (singleIfChild.isElif() || singleIfChild.elseBranch() != null || !singleIfChild.elifBranches().isEmpty()) { + return; } + addIssue(singleIfChild.keyword(), MESSAGE).secondary(ifStatement.astNode(), "enclosing"); } - return null; + super.visitIfStatement(ifStatement); } } diff --git a/python-checks/src/main/java/org/sonar/python/checks/CommentRegularExpressionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/CommentRegularExpressionCheck.java index 88b5eadb11..9fb04028d5 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/CommentRegularExpressionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/CommentRegularExpressionCheck.java @@ -24,10 +24,10 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = CommentRegularExpressionCheck.CHECK_KEY) -public class CommentRegularExpressionCheck extends PythonCheck { +public class CommentRegularExpressionCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "CommentRegularExpression"; private static final String DEFAULT_REGULAR_EXPRESSION = ""; private static final String DEFAULT_MESSAGE = "The regular expression matches this comment"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/CommentedCodeCheck.java b/python-checks/src/main/java/org/sonar/python/checks/CommentedCodeCheck.java index 3cc74c2ddc..3aec506e97 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/CommentedCodeCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/CommentedCodeCheck.java @@ -32,14 +32,14 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.PythonConfiguration; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonTokenType; import org.sonar.python.parser.PythonParser; @Rule(key = CommentedCodeCheck.CHECK_KEY) -public class CommentedCodeCheck extends PythonCheck { +public class CommentedCodeCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S125"; public static final String MESSAGE = "Remove this commented out code."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/DuplicatedMethodFieldNamesCheck.java b/python-checks/src/main/java/org/sonar/python/checks/DuplicatedMethodFieldNamesCheck.java index 189f954148..348375a494 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/DuplicatedMethodFieldNamesCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/DuplicatedMethodFieldNamesCheck.java @@ -29,12 +29,12 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.sslr.ast.AstSelect; @Rule(key = DuplicatedMethodFieldNamesCheck.CHECK_KEY) -public class DuplicatedMethodFieldNamesCheck extends PythonCheck { +public class DuplicatedMethodFieldNamesCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1845"; private static final String MESSAGE = "Rename %s \"%s\" to prevent any misunderstanding/clash with %s \"%s\" defined on line %s"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/EmptyNestedBlockCheck.java b/python-checks/src/main/java/org/sonar/python/checks/EmptyNestedBlockCheck.java index 084c3eb0b7..1f36f39387 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/EmptyNestedBlockCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/EmptyNestedBlockCheck.java @@ -27,12 +27,12 @@ import java.util.Set; import java.util.function.Predicate; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.sslr.ast.AstSelect; @Rule(key = EmptyNestedBlockCheck.CHECK_KEY) -public class EmptyNestedBlockCheck extends PythonCheck { +public class EmptyNestedBlockCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S108"; private static final Predicate NOT_PASS_PREDICATE = new NotPassPredicate(); private static final String MESSAGE = "Either remove or fill this block of code."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/ExecStatementUsageCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ExecStatementUsageCheck.java index f45452de54..0976acf394 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ExecStatementUsageCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ExecStatementUsageCheck.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = ExecStatementUsageCheck.CHECK_KEY) -public class ExecStatementUsageCheck extends PythonCheck { +public class ExecStatementUsageCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "ExecStatementUsage"; @Override public Set subscribedKinds() { diff --git a/python-checks/src/main/java/org/sonar/python/checks/ExitHasBadArgumentsCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ExitHasBadArgumentsCheck.java index 9df49c4559..85bbc96950 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ExitHasBadArgumentsCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ExitHasBadArgumentsCheck.java @@ -26,12 +26,12 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.python.IssueLocation; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; @Rule(key = ExitHasBadArgumentsCheck.CHECK_KEY) -public class ExitHasBadArgumentsCheck extends PythonCheck { +public class ExitHasBadArgumentsCheck extends PythonCheckAstNode { public static final String MESSAGE_ADD = "Add the missing argument."; public static final String MESSAGE_REMOVE = "Remove the unnecessary argument."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/FieldDuplicatesClassNameCheck.java b/python-checks/src/main/java/org/sonar/python/checks/FieldDuplicatesClassNameCheck.java index b7fbe0b8e2..fc2b94cd87 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/FieldDuplicatesClassNameCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/FieldDuplicatesClassNameCheck.java @@ -26,11 +26,11 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = FieldDuplicatesClassNameCheck.CHECK_KEY) -public class FieldDuplicatesClassNameCheck extends PythonCheck { +public class FieldDuplicatesClassNameCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1700"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/FieldNameCheck.java b/python-checks/src/main/java/org/sonar/python/checks/FieldNameCheck.java index d36bfa9f7f..15f3d0089b 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/FieldNameCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/FieldNameCheck.java @@ -28,11 +28,11 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = FieldNameCheck.CHECK_KEY) -public class FieldNameCheck extends PythonCheck { +public class FieldNameCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S116"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/FileComplexityCheck.java b/python-checks/src/main/java/org/sonar/python/checks/FileComplexityCheck.java index 0f43ce88fc..d82006247d 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/FileComplexityCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/FileComplexityCheck.java @@ -23,11 +23,11 @@ import java.text.MessageFormat; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.metrics.ComplexityVisitor; @Rule(key = "FileComplexity") -public class FileComplexityCheck extends PythonCheck { +public class FileComplexityCheck extends PythonCheckAstNode { private static final int DEFAULT_MAXIMUM_FILE_COMPLEXITY_THRESHOLD = 200; @RuleProperty( diff --git a/python-checks/src/main/java/org/sonar/python/checks/FixmeCommentCheck.java b/python-checks/src/main/java/org/sonar/python/checks/FixmeCommentCheck.java index 75a6fa51c3..0023eb483b 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/FixmeCommentCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/FixmeCommentCheck.java @@ -24,10 +24,10 @@ import com.sonar.sslr.api.Trivia; import java.util.regex.Pattern; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = FixmeCommentCheck.CHECK_KEY) -public class FixmeCommentCheck extends PythonCheck { +public class FixmeCommentCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1134"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/FunctionComplexityCheck.java b/python-checks/src/main/java/org/sonar/python/checks/FunctionComplexityCheck.java index 071eb5f2aa..0b786f1a05 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/FunctionComplexityCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/FunctionComplexityCheck.java @@ -25,12 +25,12 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.metrics.ComplexityVisitor; @Rule(key = "FunctionComplexity") -public class FunctionComplexityCheck extends PythonCheck { +public class FunctionComplexityCheck extends PythonCheckAstNode { private static final int DEFAULT_MAXIMUM_FUNCTION_COMPLEXITY_THRESHOLD = 15; private static final String MESSAGE = "Function has a complexity of %s which is greater than %s authorized."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/HardcodedIPCheck.java b/python-checks/src/main/java/org/sonar/python/checks/HardcodedIPCheck.java index f6935b9351..3744b7ed4b 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/HardcodedIPCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/HardcodedIPCheck.java @@ -29,11 +29,11 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonTokenType; @Rule(key = HardcodedIPCheck.CHECK_KEY) -public class HardcodedIPCheck extends PythonCheck { +public class HardcodedIPCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1313"; private static final String IPV4_ALONE = "(?(?:\\d{1,3}\\.){3}\\d{1,3})"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/IdenticalExpressionOnBinaryOperatorCheck.java b/python-checks/src/main/java/org/sonar/python/checks/IdenticalExpressionOnBinaryOperatorCheck.java index 1fb2527248..52c53314ed 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/IdenticalExpressionOnBinaryOperatorCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/IdenticalExpressionOnBinaryOperatorCheck.java @@ -26,11 +26,11 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = "S1764") -public class IdenticalExpressionOnBinaryOperatorCheck extends PythonCheck { +public class IdenticalExpressionOnBinaryOperatorCheck extends PythonCheckAstNode { private static final List EXCLUDED_OPERATOR_TYPES = Collections.unmodifiableList(Arrays.asList( "*", diff --git a/python-checks/src/main/java/org/sonar/python/checks/InequalityUsageCheck.java b/python-checks/src/main/java/org/sonar/python/checks/InequalityUsageCheck.java index b2cc109e34..a1cd140c26 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/InequalityUsageCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/InequalityUsageCheck.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonPunctuator; @Rule(key = InequalityUsageCheck.CHECK_KEY) -public class InequalityUsageCheck extends PythonCheck { +public class InequalityUsageCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "InequalityUsage"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/InitReturnsValueCheck.java b/python-checks/src/main/java/org/sonar/python/checks/InitReturnsValueCheck.java index 4d6c7c9c12..d6cfb5ff27 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/InitReturnsValueCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/InitReturnsValueCheck.java @@ -25,12 +25,12 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonKeyword; @Rule(key = InitReturnsValueCheck.CHECK_KEY) -public class InitReturnsValueCheck extends PythonCheck { +public class InitReturnsValueCheck extends PythonCheckAstNode { public static final String MESSAGE_RETURN = "Remove this return value."; public static final String MESSAGE_YIELD = "Remove this yield statement."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/LineLengthCheck.java b/python-checks/src/main/java/org/sonar/python/checks/LineLengthCheck.java index d572af7cb1..51414d3738 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/LineLengthCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/LineLengthCheck.java @@ -24,10 +24,10 @@ import java.text.MessageFormat; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = LineLengthCheck.CHECK_KEY) -public class LineLengthCheck extends PythonCheck { +public class LineLengthCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "LineLength"; private static final int DEFAULT_MAXIMUM_LINE_LENGTH = 120; diff --git a/python-checks/src/main/java/org/sonar/python/checks/LocalVariableAndParameterNameConventionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/LocalVariableAndParameterNameConventionCheck.java index e1b4a4e845..5d35af7487 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/LocalVariableAndParameterNameConventionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/LocalVariableAndParameterNameConventionCheck.java @@ -30,11 +30,11 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = LocalVariableAndParameterNameConventionCheck.CHECK_KEY) -public class LocalVariableAndParameterNameConventionCheck extends PythonCheck { +public class LocalVariableAndParameterNameConventionCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S117"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/LongIntegerWithLowercaseSuffixUsageCheck.java b/python-checks/src/main/java/org/sonar/python/checks/LongIntegerWithLowercaseSuffixUsageCheck.java index c55becbdbc..e5c7e86f6f 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/LongIntegerWithLowercaseSuffixUsageCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/LongIntegerWithLowercaseSuffixUsageCheck.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonTokenType; @Rule(key = LongIntegerWithLowercaseSuffixUsageCheck.CHECK_KEY) -public class LongIntegerWithLowercaseSuffixUsageCheck extends PythonCheck { +public class LongIntegerWithLowercaseSuffixUsageCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "LongIntegerWithLowercaseSuffixUsage"; private static final String MESSAGE = "Replace suffix in long integers from lower case \"l\" to upper case \"L\"."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/MethodShouldBeStaticCheck.java b/python-checks/src/main/java/org/sonar/python/checks/MethodShouldBeStaticCheck.java index 576facd8a0..e881bccee1 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/MethodShouldBeStaticCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/MethodShouldBeStaticCheck.java @@ -26,12 +26,12 @@ import java.util.Objects; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonTokenType; @Rule(key = MethodShouldBeStaticCheck.CHECK_KEY) -public class MethodShouldBeStaticCheck extends PythonCheck { +public class MethodShouldBeStaticCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S2325"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/MissingDocstringCheck.java b/python-checks/src/main/java/org/sonar/python/checks/MissingDocstringCheck.java index 219d8312f4..1c1edd85e2 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/MissingDocstringCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/MissingDocstringCheck.java @@ -26,11 +26,11 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.python.DocstringExtractor; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = MissingDocstringCheck.CHECK_KEY) -public class MissingDocstringCheck extends PythonCheck { +public class MissingDocstringCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1720"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/MissingNewlineAtEndOfFileCheck.java b/python-checks/src/main/java/org/sonar/python/checks/MissingNewlineAtEndOfFileCheck.java index ceef1818bd..259fa1e9c4 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/MissingNewlineAtEndOfFileCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/MissingNewlineAtEndOfFileCheck.java @@ -22,10 +22,10 @@ import com.sonar.sslr.api.AstNode; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = MissingNewlineAtEndOfFileCheck.CHECK_KEY) -public class MissingNewlineAtEndOfFileCheck extends PythonCheck { +public class MissingNewlineAtEndOfFileCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S113"; public static final String MESSAGE = "Add a new line at the end of this file \"%s\"."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/ModuleNameCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ModuleNameCheck.java index 279b4666b9..cc48453bb1 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ModuleNameCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ModuleNameCheck.java @@ -24,10 +24,10 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = ModuleNameCheck.CHECK_KEY) -public class ModuleNameCheck extends PythonCheck { +public class ModuleNameCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1578"; private static final String DEFAULT = "(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/NeedlessPassCheck.java b/python-checks/src/main/java/org/sonar/python/checks/NeedlessPassCheck.java index a6f1c97bea..403da3ba23 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/NeedlessPassCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/NeedlessPassCheck.java @@ -25,13 +25,13 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonTokenType; import org.sonar.sslr.ast.AstSelect; @Rule(key = NeedlessPassCheck.CHECK_KEY) -public class NeedlessPassCheck extends PythonCheck { +public class NeedlessPassCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S2772"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/NestedControlFlowDepthCheck.java b/python-checks/src/main/java/org/sonar/python/checks/NestedControlFlowDepthCheck.java index 6954f08b3c..0162570cf7 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/NestedControlFlowDepthCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/NestedControlFlowDepthCheck.java @@ -27,11 +27,11 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = NestedControlFlowDepthCheck.CHECK_KEY) -public class NestedControlFlowDepthCheck extends PythonCheck { +public class NestedControlFlowDepthCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S134"; private static final int DEFAULT_MAX = 4; diff --git a/python-checks/src/main/java/org/sonar/python/checks/NewStyleClassCheck.java b/python-checks/src/main/java/org/sonar/python/checks/NewStyleClassCheck.java index bd7db83a89..e1f7fa2b3a 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/NewStyleClassCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/NewStyleClassCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = NewStyleClassCheck.CHECK_KEY) -public class NewStyleClassCheck extends PythonCheck { +public class NewStyleClassCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1722"; private static final String MESSAGE = "Add inheritance from \"object\" or some other new-style class."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/NoPersonReferenceInTodoCheck.java b/python-checks/src/main/java/org/sonar/python/checks/NoPersonReferenceInTodoCheck.java index 02b3c51266..a2f0897e5c 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/NoPersonReferenceInTodoCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/NoPersonReferenceInTodoCheck.java @@ -26,10 +26,10 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = NoPersonReferenceInTodoCheck.CHECK_KEY) -public class NoPersonReferenceInTodoCheck extends PythonCheck { +public class NoPersonReferenceInTodoCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1707"; public static final String MESSAGE = "Add a citation of the person who can best explain this comment."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/OneStatementPerLineCheck.java b/python-checks/src/main/java/org/sonar/python/checks/OneStatementPerLineCheck.java index 9a5085cdfb..64bd8b50f0 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/OneStatementPerLineCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/OneStatementPerLineCheck.java @@ -25,14 +25,14 @@ import java.util.Map; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; /** * Note that implementation differs from AbstractOneStatementPerLineCheck due to Python specifics */ @Rule(key = OneStatementPerLineCheck.CHECK_KEY) -public class OneStatementPerLineCheck extends PythonCheck { +public class OneStatementPerLineCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "OneStatementPerLine"; private final Map statementsPerLine = new HashMap<>(); diff --git a/python-checks/src/main/java/org/sonar/python/checks/ParsingErrorCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ParsingErrorCheck.java index f0b53f98f1..87085bebc4 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ParsingErrorCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ParsingErrorCheck.java @@ -21,11 +21,11 @@ import com.sonar.sslr.api.RecognitionException; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.PythonVisitorContext; @Rule(key = ParsingErrorCheck.CHECK_KEY) -public class ParsingErrorCheck extends PythonCheck { +public class ParsingErrorCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "ParsingError"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/PreIncrementDecrementCheck.java b/python-checks/src/main/java/org/sonar/python/checks/PreIncrementDecrementCheck.java index 083e03d538..17f67dd351 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/PreIncrementDecrementCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/PreIncrementDecrementCheck.java @@ -25,12 +25,12 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; @Rule(key = PreIncrementDecrementCheck.CHECK_KEY) -public class PreIncrementDecrementCheck extends PythonCheck { +public class PreIncrementDecrementCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "PreIncrementDecrement"; private static final String MESSAGE = "This statement doesn't produce the expected result, replace use of non-existent pre-%srement operator"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/PrintStatementUsageCheck.java b/python-checks/src/main/java/org/sonar/python/checks/PrintStatementUsageCheck.java index ec555eadc0..e463c7a316 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/PrintStatementUsageCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/PrintStatementUsageCheck.java @@ -24,11 +24,11 @@ import java.util.Collections; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = PrintStatementUsageCheck.CHECK_KEY) -public class PrintStatementUsageCheck extends PythonCheck { +public class PrintStatementUsageCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "PrintStatementUsage"; @Override diff --git a/python-checks/src/main/java/org/sonar/python/checks/ReturnAndYieldInOneFunctionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ReturnAndYieldInOneFunctionCheck.java index a9717d9022..51dac61d92 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ReturnAndYieldInOneFunctionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ReturnAndYieldInOneFunctionCheck.java @@ -25,11 +25,11 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = ReturnAndYieldInOneFunctionCheck.CHECK_KEY) -public class ReturnAndYieldInOneFunctionCheck extends PythonCheck { +public class ReturnAndYieldInOneFunctionCheck extends PythonCheckAstNode { public static final String MESSAGE = "Use only \"return\" or only \"yield\", not both."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/ReturnYieldOutsideFunctionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/ReturnYieldOutsideFunctionCheck.java index ef95ea658a..de436928f5 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/ReturnYieldOutsideFunctionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/ReturnYieldOutsideFunctionCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = ReturnYieldOutsideFunctionCheck.CHECK_KEY) -public class ReturnYieldOutsideFunctionCheck extends PythonCheck { +public class ReturnYieldOutsideFunctionCheck extends PythonCheckAstNode { public static final String MESSAGE = "Remove this use of \"%s\"."; public static final String CHECK_KEY = "S2711"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/SameBranchCheck.java b/python-checks/src/main/java/org/sonar/python/checks/SameBranchCheck.java index dc40b8e098..8450e56ff6 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/SameBranchCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/SameBranchCheck.java @@ -28,7 +28,7 @@ import javax.annotation.Nullable; import org.sonar.check.Rule; import org.sonar.python.IssueLocation; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonKeyword; import org.sonar.python.api.PythonPunctuator; @@ -36,7 +36,7 @@ import org.sonar.sslr.ast.AstSelect; @Rule(key = SameBranchCheck.CHECK_KEY) -public class SameBranchCheck extends PythonCheck { +public class SameBranchCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1871"; public static final String MESSAGE = "Either merge this branch with the identical one on line \"%s\" or change one of the implementations."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/SameConditionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/SameConditionCheck.java index ba8257c1f8..677b7d3007 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/SameConditionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/SameConditionCheck.java @@ -27,13 +27,13 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonKeyword; import org.sonar.sslr.ast.AstSelect; @Rule(key = SameConditionCheck.CHECK_KEY) -public class SameConditionCheck extends PythonCheck { +public class SameConditionCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1862"; private static final String MESSAGE = "This branch duplicates the one on line %s."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/SelfAssignmentCheck.java b/python-checks/src/main/java/org/sonar/python/checks/SelfAssignmentCheck.java index cfedd11bac..45efb0b899 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/SelfAssignmentCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/SelfAssignmentCheck.java @@ -25,13 +25,13 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.python.PythonBuiltinFunctions; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonKeyword; import org.sonar.python.api.PythonPunctuator; @Rule(key = SelfAssignmentCheck.CHECK_KEY) -public class SelfAssignmentCheck extends PythonCheck { +public class SelfAssignmentCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1656"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/TooManyLinesInFileCheck.java b/python-checks/src/main/java/org/sonar/python/checks/TooManyLinesInFileCheck.java index 02fb5f9072..d10a643e98 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/TooManyLinesInFileCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/TooManyLinesInFileCheck.java @@ -27,10 +27,10 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = TooManyLinesInFileCheck.CHECK_KEY) -public class TooManyLinesInFileCheck extends PythonCheck { +public class TooManyLinesInFileCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S104"; private static final int DEFAULT = 1000; diff --git a/python-checks/src/main/java/org/sonar/python/checks/TooManyParametersCheck.java b/python-checks/src/main/java/org/sonar/python/checks/TooManyParametersCheck.java index a5b2af3253..055871b5d2 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/TooManyParametersCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/TooManyParametersCheck.java @@ -24,11 +24,11 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = TooManyParametersCheck.CHECK_KEY) -public class TooManyParametersCheck extends PythonCheck { +public class TooManyParametersCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S107"; private static final String MESSAGE = "%s has %s parameters, which is greater than the %s authorized."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/TooManyReturnsCheck.java b/python-checks/src/main/java/org/sonar/python/checks/TooManyReturnsCheck.java index 0a4f99a9a7..1fb9cadd0b 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/TooManyReturnsCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/TooManyReturnsCheck.java @@ -27,12 +27,12 @@ import java.util.Set; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.sslr.ast.AstSelect; @Rule(key = TooManyReturnsCheck.CHECK_KEY) -public class TooManyReturnsCheck extends PythonCheck { +public class TooManyReturnsCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1142"; private static final int DEFAULT_MAX = 3; diff --git a/python-checks/src/main/java/org/sonar/python/checks/TrailingCommentCheck.java b/python-checks/src/main/java/org/sonar/python/checks/TrailingCommentCheck.java index a3607ee9b9..6d624ef69c 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/TrailingCommentCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/TrailingCommentCheck.java @@ -25,10 +25,10 @@ import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = TrailingCommentCheck.CHECK_KEY) -public class TrailingCommentCheck extends PythonCheck { +public class TrailingCommentCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S139"; private static final String DEFAULT_LEGAL_COMMENT_PATTERN = "^#\\s*+[^\\s]++$"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/TrailingWhitespaceCheck.java b/python-checks/src/main/java/org/sonar/python/checks/TrailingWhitespaceCheck.java index 549df311b8..b0af7c58f4 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/TrailingWhitespaceCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/TrailingWhitespaceCheck.java @@ -23,10 +23,10 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = TrailingWhitespaceCheck.CHECK_KEY) -public class TrailingWhitespaceCheck extends PythonCheck { +public class TrailingWhitespaceCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1131"; public static final String MESSAGE = "Remove the useless trailing whitespaces at the end of this line."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/UnusedLocalVariableCheck.java b/python-checks/src/main/java/org/sonar/python/checks/UnusedLocalVariableCheck.java index e7990ed9a1..f8bba27464 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/UnusedLocalVariableCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/UnusedLocalVariableCheck.java @@ -28,12 +28,12 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.semantic.Symbol; @Rule(key = "S1481") -public class UnusedLocalVariableCheck extends PythonCheck { +public class UnusedLocalVariableCheck extends PythonCheckAstNode { private static final Pattern IDENTIFIER_SEPARATOR = Pattern.compile("[^a-zA-Z0-9_]+"); diff --git a/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisAfterKeywordCheck.java b/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisAfterKeywordCheck.java index 775d2dcb2c..1fb535fe80 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisAfterKeywordCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisAfterKeywordCheck.java @@ -28,12 +28,12 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; @Rule(key = UselessParenthesisAfterKeywordCheck.CHECK_KEY) -public class UselessParenthesisAfterKeywordCheck extends PythonCheck { +public class UselessParenthesisAfterKeywordCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1721"; private static final Map KEYWORDS_FOLLOWED_BY_TEST = initializeKeywordsFollowedByTest(); diff --git a/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisCheck.java b/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisCheck.java index bee3545b10..f83b8e6765 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/UselessParenthesisCheck.java @@ -25,13 +25,13 @@ import java.util.List; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonKeyword; import org.sonar.python.api.PythonPunctuator; @Rule(key = UselessParenthesisCheck.CHECK_KEY) -public class UselessParenthesisCheck extends PythonCheck { +public class UselessParenthesisCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1110"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/XPathCheck.java b/python-checks/src/main/java/org/sonar/python/checks/XPathCheck.java index dbc3118f77..7fe4fcb7d6 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/XPathCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/XPathCheck.java @@ -24,10 +24,10 @@ import javax.annotation.CheckForNull; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; @Rule(key = XPathCheck.CHECK_KEY) -public class XPathCheck extends PythonCheck { +public class XPathCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "XPath"; private static final String DEFAULT_XPATH_QUERY = ""; private static final String DEFAULT_MESSAGE = "The XPath expression matches this piece of code"; diff --git a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java index 4ac70849c3..37b8799b73 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java @@ -23,13 +23,13 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; import org.sonar.python.semantic.Symbol; @Rule(key = DebugModeCheck.CHECK_KEY) -public class DebugModeCheck extends PythonCheck { +public class DebugModeCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S4507"; private static final String MESSAGE = "Make sure this debug feature is deactivated before delivering the code in production."; private static final Set debugProperties = immutableSet("DEBUG", "DEBUG_PROPAGATE_EXCEPTIONS"); diff --git a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DynamicCodeExecutionCheck.java b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DynamicCodeExecutionCheck.java index 94cfb7e442..1a61a4a131 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DynamicCodeExecutionCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DynamicCodeExecutionCheck.java @@ -23,11 +23,11 @@ import com.sonar.sslr.api.AstNodeType; import java.util.Set; import org.sonar.check.Rule; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; @Rule(key = DynamicCodeExecutionCheck.CHECK_KEY) -public class DynamicCodeExecutionCheck extends PythonCheck { +public class DynamicCodeExecutionCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S1523"; private static final String MESSAGE = "Make sure that this dynamic injection or execution of code is safe."; diff --git a/python-checks/src/main/java/org/sonar/python/checks/hotspots/RegexCheck.java b/python-checks/src/main/java/org/sonar/python/checks/hotspots/RegexCheck.java index a1d9da891a..5445216355 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/hotspots/RegexCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/hotspots/RegexCheck.java @@ -25,13 +25,13 @@ import javax.annotation.CheckForNull; import org.sonar.check.Rule; import org.sonar.python.IssueLocation; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.PythonPunctuator; import org.sonar.python.semantic.Symbol; @Rule(key = RegexCheck.CHECK_KEY) -public class RegexCheck extends PythonCheck { +public class RegexCheck extends PythonCheckAstNode { public static final String CHECK_KEY = "S4784"; private static final String MESSAGE = "Make sure that using a regular expression is safe here."; private static final int REGEX_ARGUMENT = 0; diff --git a/python-checks/src/test/java/org/sonar/python/checks/CheckUtilsTest.java b/python-checks/src/test/java/org/sonar/python/checks/CheckUtilsTest.java index c555b60490..b327483196 100644 --- a/python-checks/src/test/java/org/sonar/python/checks/CheckUtilsTest.java +++ b/python-checks/src/test/java/org/sonar/python/checks/CheckUtilsTest.java @@ -30,7 +30,7 @@ import java.util.function.Predicate; import java.util.function.UnaryOperator; import org.junit.Test; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.PythonConfiguration; import org.sonar.python.api.PythonGrammar; import org.sonar.python.checks.utils.PythonCheckVerifier; @@ -57,7 +57,7 @@ public void equals_node() { PythonCheckVerifier.verify("src/test/resources/checks/utils/equalsNode.py", new EqualsNodeCheck()); } - private static class EqualsNodeCheck extends PythonCheck { + private static class EqualsNodeCheck extends PythonCheckAstNode { @Override public Set subscribedKinds() { return ImmutableSet.copyOf(PythonGrammar.values()); @@ -81,7 +81,7 @@ public void is_method_definition() { PythonCheckVerifier.verify("src/test/resources/checks/utils/isMethodDefinition.py", new IsMethodDefinitionCheck()); } - private static class IsMethodDefinitionCheck extends PythonCheck { + private static class IsMethodDefinitionCheck extends PythonCheckAstNode { @Override public Set subscribedKinds() { return ImmutableSet.of( @@ -106,7 +106,7 @@ public void class_has_inheritance() { PythonCheckVerifier.verify("src/test/resources/checks/utils/classHasInheritance.py", new ClassHasInheritanceCheck()); } - private static class ClassHasInheritanceCheck extends PythonCheck { + private static class ClassHasInheritanceCheck extends PythonCheckAstNode { @Override public Set subscribedKinds() { return ImmutableSet.of(PythonGrammar.CLASSDEF); diff --git a/python-squid/src/main/java/org/sonar/python/IssueLocation.java b/python-squid/src/main/java/org/sonar/python/IssueLocation.java index 3a00f0eba1..380b74766e 100644 --- a/python-squid/src/main/java/org/sonar/python/IssueLocation.java +++ b/python-squid/src/main/java/org/sonar/python/IssueLocation.java @@ -52,6 +52,10 @@ public static IssueLocation preciseLocation(AstNode startNode, @Nullable String return new PreciseIssueLocation(startNode, message); } + public static IssueLocation preciseLocation(Token token, @Nullable String message) { + return new PreciseIssueLocation(token, message); + } + @CheckForNull public String message() { return message; @@ -82,6 +86,12 @@ public PreciseIssueLocation(AstNode startNode, AstNode endNode, String message) this.lastTokenLocation = new TokenLocation(endNode.getLastToken()); } + public PreciseIssueLocation(Token token, String message) { + super(message); + this.firstToken = token; + this.lastTokenLocation = new TokenLocation(token); + } + @Override public int startLine() { return firstToken.getLine(); diff --git a/python-squid/src/main/java/org/sonar/python/PythonCheck.java b/python-squid/src/main/java/org/sonar/python/PythonCheck.java index 4423ed33da..98d59cf5d2 100644 --- a/python-squid/src/main/java/org/sonar/python/PythonCheck.java +++ b/python-squid/src/main/java/org/sonar/python/PythonCheck.java @@ -20,53 +20,23 @@ package org.sonar.python; import com.sonar.sslr.api.AstNode; -import com.sonar.sslr.api.Token; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.annotation.Nullable; -public abstract class PythonCheck extends PythonVisitor { +public interface PythonCheck { - protected final PreciseIssue addIssue(AstNode node, @Nullable String message) { - PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.preciseLocation(node, message)); - getContext().addIssue(newIssue); - return newIssue; - } - - protected final PreciseIssue addIssue(IssueLocation primaryLocation) { - PreciseIssue newIssue = new PreciseIssue(this, primaryLocation); - getContext().addIssue(newIssue); - return newIssue; - } - - protected final PreciseIssue addLineIssue(String message, int lineNumber) { - PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.atLineLevel(message, lineNumber)); - getContext().addIssue(newIssue); - return newIssue; - } - protected final PreciseIssue addFileIssue(String message) { - PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.atFileLevel(message)); - getContext().addIssue(newIssue); - return newIssue; - } - - protected final PreciseIssue addIssue(Token token, String message) { - return addIssue(new AstNode(token), message); - } + void scanFile(PythonVisitorContext visitorContext); - public static class PreciseIssue { + class PreciseIssue { private final PythonCheck check; private final IssueLocation primaryLocation; private Integer cost; private final List secondaryLocations; - private PreciseIssue(PythonCheck check, IssueLocation primaryLocation) { + protected PreciseIssue(PythonCheck check, IssueLocation primaryLocation) { this.check = check; this.primaryLocation = primaryLocation; this.secondaryLocations = new ArrayList<>(); @@ -104,8 +74,4 @@ public PythonCheck check() { return check; } } - - public static Set immutableSet(T... el) { - return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(el))); - } } diff --git a/python-squid/src/main/java/org/sonar/python/PythonCheckAstNode.java b/python-squid/src/main/java/org/sonar/python/PythonCheckAstNode.java new file mode 100644 index 0000000000..521cf04547 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/PythonCheckAstNode.java @@ -0,0 +1,63 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.Nullable; + +public abstract class PythonCheckAstNode extends PythonVisitor implements PythonCheck { + + protected final PreciseIssue addIssue(AstNode node, @Nullable String message) { + PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.preciseLocation(node, message)); + getContext().addIssue(newIssue); + return newIssue; + } + + protected final PreciseIssue addIssue(IssueLocation primaryLocation) { + PreciseIssue newIssue = new PreciseIssue(this, primaryLocation); + getContext().addIssue(newIssue); + return newIssue; + } + + protected final PreciseIssue addLineIssue(String message, int lineNumber) { + PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.atLineLevel(message, lineNumber)); + getContext().addIssue(newIssue); + return newIssue; + } + + protected final PreciseIssue addFileIssue(String message) { + PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.atFileLevel(message)); + getContext().addIssue(newIssue); + return newIssue; + } + + protected final PreciseIssue addIssue(Token token, String message) { + return addIssue(new AstNode(token), message); + } + + public static Set immutableSet(T... el) { + return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(el))); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/PythonCheckTree.java b/python-squid/src/main/java/org/sonar/python/PythonCheckTree.java new file mode 100644 index 0000000000..23a85739e5 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/PythonCheckTree.java @@ -0,0 +1,52 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python; + +import com.sonar.sslr.api.Token; +import javax.annotation.Nullable; +import org.sonar.python.api.tree.Tree; +import org.sonar.python.tree.BaseTreeVisitor; + +public abstract class PythonCheckTree extends BaseTreeVisitor implements PythonCheck { + + private PythonVisitorContext context; + + private PythonVisitorContext getContext() { + return context; + } + + protected final PreciseIssue addIssue(Token token, @Nullable String message) { + PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.preciseLocation(token, message)); + getContext().addIssue(newIssue); + return newIssue; + } + + protected final PreciseIssue addIssue(Tree node, @Nullable String message) { + PreciseIssue newIssue = new PreciseIssue(this, IssueLocation.preciseLocation(node.astNode(), message)); + getContext().addIssue(newIssue); + return newIssue; + } + + @Override + public void scanFile(PythonVisitorContext visitorContext) { + this.context = visitorContext; + scan(context.rootTree()); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/PythonVisitor.java b/python-squid/src/main/java/org/sonar/python/PythonVisitor.java index fccd2b9868..3cc131d4ba 100644 --- a/python-squid/src/main/java/org/sonar/python/PythonVisitor.java +++ b/python-squid/src/main/java/org/sonar/python/PythonVisitor.java @@ -60,7 +60,7 @@ public PythonVisitorContext getContext() { public void scanFile(PythonVisitorContext context) { this.context = context; - AstNode tree = context.rootTree(); + AstNode tree = context.rootAstNode(); if (tree != null) { visitFile(tree); scanNode(tree, subscribedKinds()); diff --git a/python-squid/src/main/java/org/sonar/python/PythonVisitorContext.java b/python-squid/src/main/java/org/sonar/python/PythonVisitorContext.java index 2d49a3b37e..b608333810 100644 --- a/python-squid/src/main/java/org/sonar/python/PythonVisitorContext.java +++ b/python-squid/src/main/java/org/sonar/python/PythonVisitorContext.java @@ -24,38 +24,47 @@ import java.util.ArrayList; import java.util.List; import org.sonar.python.PythonCheck.PreciseIssue; +import org.sonar.python.api.tree.PyFileInputTree; import org.sonar.python.semantic.SymbolTable; import org.sonar.python.semantic.SymbolTableBuilderVisitor; +import org.sonar.python.tree.PyFileInputTreeImpl; public class PythonVisitorContext { - private final AstNode rootTree; + private final PyFileInputTreeImpl rootTree; private final PythonFile pythonFile; private final RecognitionException parsingException; + private final AstNode rootAst; private SymbolTable symbolTable = null; private List issues = new ArrayList<>(); - public PythonVisitorContext(AstNode rootTree, PythonFile pythonFile) { - this(rootTree, pythonFile, null); + + public PythonVisitorContext(AstNode rootAst, PyFileInputTree rootTree, PythonFile pythonFile) { + this(rootAst, rootTree, pythonFile, null); SymbolTableBuilderVisitor symbolTableBuilderVisitor = new SymbolTableBuilderVisitor(); symbolTableBuilderVisitor.scanFile(this); symbolTable = symbolTableBuilderVisitor.symbolTable(); } public PythonVisitorContext(PythonFile pythonFile, RecognitionException parsingException) { - this(null, pythonFile, parsingException); + this(null, null, pythonFile, parsingException); } - private PythonVisitorContext(AstNode rootTree, PythonFile pythonFile, RecognitionException parsingException) { - this.rootTree = rootTree; + private PythonVisitorContext(AstNode rootAst, PyFileInputTree rootTree, PythonFile pythonFile, RecognitionException parsingException) { + this.rootAst = rootAst; + this.rootTree = (PyFileInputTreeImpl) rootTree; this.pythonFile = pythonFile; this.parsingException = parsingException; } - public AstNode rootTree() { + public PyFileInputTree rootTree() { return rootTree; } + public AstNode rootAstNode() { + return rootAst; + } + public PythonFile pythonFile() { return pythonFile; } diff --git a/python-squid/src/main/java/org/sonar/python/TestPythonVisitorRunner.java b/python-squid/src/main/java/org/sonar/python/TestPythonVisitorRunner.java index b593f58417..de4601919b 100644 --- a/python-squid/src/main/java/org/sonar/python/TestPythonVisitorRunner.java +++ b/python-squid/src/main/java/org/sonar/python/TestPythonVisitorRunner.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import org.sonar.python.api.tree.PyFileInputTree; +import org.sonar.python.tree.PythonTreeMaker; import org.sonar.python.parser.PythonParser; public class TestPythonVisitorRunner { @@ -43,8 +45,9 @@ public static void scanFile(File file, PythonVisitor... visitors) { public static PythonVisitorContext createContext(File file) { Parser parser = PythonParser.create(new PythonConfiguration(StandardCharsets.UTF_8)); TestPythonFile pythonFile = new TestPythonFile(file); - AstNode rootTree = parser.parse(pythonFile.content()); - return new PythonVisitorContext(rootTree, pythonFile); + AstNode astNode = parser.parse(pythonFile.content()); + PyFileInputTree rootTree = new PythonTreeMaker().fileInput(astNode); + return new PythonVisitorContext(astNode, rootTree, pythonFile); } private static class TestPythonFile implements PythonFile { diff --git a/python-squid/src/main/java/org/sonar/python/api/PythonGrammar.java b/python-squid/src/main/java/org/sonar/python/api/PythonGrammar.java index 07d58646a7..fc42d1108d 100644 --- a/python-squid/src/main/java/org/sonar/python/api/PythonGrammar.java +++ b/python-squid/src/main/java/org/sonar/python/api/PythonGrammar.java @@ -351,11 +351,17 @@ public static void simpleStatements(LexerfulGrammarBuilder b) { b.rule(IMPORT_STMT).is(b.firstOf(IMPORT_NAME, IMPORT_FROM)); b.rule(IMPORT_NAME).is("import", DOTTED_AS_NAMES); - b.rule(IMPORT_FROM).is("from", b.firstOf(b.sequence(b.zeroOrMore("."), DOTTED_NAME), b.oneOrMore(".")), "import", + b.rule(IMPORT_FROM).is( + "from", + b.firstOf( + b.sequence(b.zeroOrMore("."), DOTTED_NAME), + b.oneOrMore(".")), + "import", b.firstOf("*", b.sequence("(", IMPORT_AS_NAMES, ")"), IMPORT_AS_NAMES)); b.rule(IMPORT_AS_NAME).is(NAME, b.optional("as", NAME)); b.rule(DOTTED_AS_NAME).is(DOTTED_NAME, b.optional("as", NAME)); - b.rule(IMPORT_AS_NAMES).is(IMPORT_AS_NAME, b.zeroOrMore(",", IMPORT_AS_NAME), b.optional(",")); + b.rule(IMPORT_AS_NAMES).is(IMPORT_AS_NAME, b.zeroOrMore(",", IMPORT_AS_NAME), + b.optional(",")); b.rule(DOTTED_AS_NAMES).is(DOTTED_AS_NAME, b.zeroOrMore(",", DOTTED_AS_NAME)); b.rule(GLOBAL_STMT).is("global", NAME, b.zeroOrMore(",", NAME)); diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyAliasedNameTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyAliasedNameTree.java new file mode 100644 index 0000000000..339afe09fc --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyAliasedNameTree.java @@ -0,0 +1,40 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import javax.annotation.CheckForNull; + +/** + * Aliased name + * + *
+ * {@link PyNameTree#name()} {@link #asKeyword()} {@link #alias()}
+ * 
+ */ +public interface PyAliasedNameTree extends Tree { + @CheckForNull + Token asKeyword(); + + @CheckForNull + PyNameTree alias(); + + PyDottedNameTree dottedName(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyArgListTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyArgListTree.java new file mode 100644 index 0000000000..bee30279fd --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyArgListTree.java @@ -0,0 +1,23 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyArgListTree extends Tree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyAssertStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyAssertStatementTree.java new file mode 100644 index 0000000000..e29ec0b229 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyAssertStatementTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyAssertStatementTree extends PyStatementTree { + Token assertKeyword(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyBreakStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyBreakStatementTree.java new file mode 100644 index 0000000000..bb6aaab9a6 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyBreakStatementTree.java @@ -0,0 +1,26 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; + +public interface PyBreakStatementTree extends PyStatementTree { + Token breakKeyword(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyClassDefTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyClassDefTree.java new file mode 100644 index 0000000000..f29da34262 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyClassDefTree.java @@ -0,0 +1,46 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyClassDefTree extends PyStatementTree { + + List decorators(); + + Token classKeyword(); + + PyNameTree name(); + + @CheckForNull + Token leftPar(); + + @CheckForNull + PyArgListTree args(); + + @CheckForNull + Token rightPar(); + + Token colon(); + + List body(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyContinueStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyContinueStatementTree.java new file mode 100644 index 0000000000..fdc3494af9 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyContinueStatementTree.java @@ -0,0 +1,26 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; + +public interface PyContinueStatementTree extends PyStatementTree { + Token continueKeyword(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyDecoratorTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyDecoratorTree.java new file mode 100644 index 0000000000..db0c101ce5 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyDecoratorTree.java @@ -0,0 +1,23 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyDecoratorTree extends Tree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyDelStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyDelStatementTree.java new file mode 100644 index 0000000000..f07fc79c55 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyDelStatementTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyDelStatementTree extends PyStatementTree { + Token delKeyword(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyDottedNameTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyDottedNameTree.java new file mode 100644 index 0000000000..4af1b51c3d --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyDottedNameTree.java @@ -0,0 +1,27 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import java.util.List; + +public interface PyDottedNameTree extends Tree { + + List names(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyElseStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyElseStatementTree.java new file mode 100644 index 0000000000..b5e6f60402 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyElseStatementTree.java @@ -0,0 +1,30 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyElseStatementTree extends PyStatementTree { + Token elseKeyword(); + + List body(); + +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyExceptClauseTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyExceptClauseTree.java new file mode 100644 index 0000000000..50ff4b53be --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyExceptClauseTree.java @@ -0,0 +1,42 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyExceptClauseTree extends Tree { + Token exceptKeyword(); + + List body(); + + @CheckForNull + Token asKeyword(); + + @CheckForNull + Token commaToken(); + + @CheckForNull + PyExpressionTree exception(); + + @CheckForNull + PyExpressionTree exceptionInstance(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyExecStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyExecStatementTree.java new file mode 100644 index 0000000000..50eae73b8a --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyExecStatementTree.java @@ -0,0 +1,32 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; + +public interface PyExecStatementTree extends PyStatementTree { + Token execKeyword(); + + PyExpressionTree expression(); + + PyExpressionTree globalsExpression(); + + PyExpressionTree localsExpression(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionStatementTree.java new file mode 100644 index 0000000000..8327c2252f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionStatementTree.java @@ -0,0 +1,26 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import java.util.List; + +public interface PyExpressionStatementTree extends PyStatementTree { + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionTree.java new file mode 100644 index 0000000000..2e918c6198 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyExpressionTree.java @@ -0,0 +1,23 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyExpressionTree extends Tree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyFileInputTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyFileInputTree.java new file mode 100644 index 0000000000..639be1b810 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyFileInputTree.java @@ -0,0 +1,26 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import java.util.List; + +public interface PyFileInputTree extends Tree { + List statements(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyFinallyClauseTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyFinallyClauseTree.java new file mode 100644 index 0000000000..3d24236d4e --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyFinallyClauseTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyFinallyClauseTree extends Tree { + Token finallyKeyword(); + + List body(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyForStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyForStatementTree.java new file mode 100644 index 0000000000..b8366fed88 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyForStatementTree.java @@ -0,0 +1,51 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyForStatementTree extends PyStatementTree { + Token forKeyword(); + + List expressions(); + + Token inKeyword(); + + List testExpressions(); + + Token colon(); + + List body(); + + @CheckForNull + Token elseKeyword(); + + @CheckForNull + Token elseColon(); + + List elseBody(); + + boolean isAsync(); + + @CheckForNull + Token asyncKeyword(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyFunctionDefTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyFunctionDefTree.java new file mode 100644 index 0000000000..b5c095052f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyFunctionDefTree.java @@ -0,0 +1,61 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyFunctionDefTree extends PyStatementTree { + + List decorators(); + + Token defKeyword(); + + @CheckForNull + Token asyncKeyword(); + + PyNameTree name(); + + Token leftPar(); + + PyTypedArgListTree typedArgs(); + + Token rightPar(); + + /** + * dash of optional '->' + */ + @CheckForNull + Token dash(); + /** + * pointer of optional '->' + */ + @CheckForNull + Token gt(); + + @CheckForNull + PyExpressionTree annotationReturn(); + + Token colon(); + + List body(); + +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyGlobalStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyGlobalStatementTree.java new file mode 100644 index 0000000000..dd12977f8b --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyGlobalStatementTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyGlobalStatementTree extends PyStatementTree { + Token globalKeyword(); + + List variables(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyIfStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyIfStatementTree.java new file mode 100644 index 0000000000..fcdab0d9f5 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyIfStatementTree.java @@ -0,0 +1,43 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +/** + * if-elif-else statement + */ +public interface PyIfStatementTree extends PyStatementTree { + Token keyword(); + + PyExpressionTree condition(); + + List body(); + + List elifBranches(); + + boolean isElif(); + + @CheckForNull + PyElseStatementTree elseBranch(); + +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyImportFromTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportFromTree.java new file mode 100644 index 0000000000..0ee4d316e6 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportFromTree.java @@ -0,0 +1,51 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +/** + * Import From statement + * + *
+ *   {@link #fromKeyword()} {@link #dottedPrefixForModule()} {@link #module()} {@link #importKeyword()} {@link #importedNames()}
+ * 
+ */ +public interface PyImportFromTree extends PyImportStatementTree { + Token fromKeyword(); + + @CheckForNull + PyDottedNameTree module(); + + Token importKeyword(); + + @CheckForNull + List dottedPrefixForModule(); + + @CheckForNull + List importedNames(); + + boolean isWildcardImport(); + + @CheckForNull + Token wildcard(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyImportNameTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportNameTree.java new file mode 100644 index 0000000000..f2c0b86a56 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportNameTree.java @@ -0,0 +1,36 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +/** + * Import statement + * + *
+ *   {@link #importKeyword()} {@link #modules()}}
+ * 
+ */ +public interface PyImportNameTree extends PyImportStatementTree { + Token importKeyword(); + + List modules(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyImportStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportStatementTree.java new file mode 100644 index 0000000000..0b0a00241e --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyImportStatementTree.java @@ -0,0 +1,27 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +/** + * Import statement + * + */ +public interface PyImportStatementTree extends PyStatementTree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyNameTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyNameTree.java new file mode 100644 index 0000000000..bd0e47d976 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyNameTree.java @@ -0,0 +1,25 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyNameTree extends Tree { + + String name(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyNonlocalStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyNonlocalStatementTree.java new file mode 100644 index 0000000000..fa99a13f6d --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyNonlocalStatementTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyNonlocalStatementTree extends PyStatementTree { + Token nonlocalKeyword(); + + List variables(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyPassStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyPassStatementTree.java new file mode 100644 index 0000000000..d256d2688c --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyPassStatementTree.java @@ -0,0 +1,26 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; + +public interface PyPassStatementTree extends PyStatementTree { + Token passKeyword(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyPrintStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyPrintStatementTree.java new file mode 100644 index 0000000000..3ac93c6290 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyPrintStatementTree.java @@ -0,0 +1,30 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyPrintStatementTree extends PyStatementTree { + + Token printKeyword(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyRaiseStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyRaiseStatementTree.java new file mode 100644 index 0000000000..1c664f15ef --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyRaiseStatementTree.java @@ -0,0 +1,36 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyRaiseStatementTree extends PyStatementTree { + Token raiseKeyword(); + + @CheckForNull + Token fromKeyword(); + + @CheckForNull + PyExpressionTree fromExpression(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyReturnStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyReturnStatementTree.java new file mode 100644 index 0000000000..5af5a8e26a --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyReturnStatementTree.java @@ -0,0 +1,29 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; + +public interface PyReturnStatementTree extends PyStatementTree { + Token returnKeyword(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyStatementTree.java new file mode 100644 index 0000000000..f2645d08da --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyStatementTree.java @@ -0,0 +1,23 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyStatementTree extends Tree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java new file mode 100644 index 0000000000..ac74be4a18 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java @@ -0,0 +1,85 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyTreeVisitor { + + void visitFileInput(PyFileInputTree pyFileInputTree); + + void visitIfStatement(PyIfStatementTree pyIfStatementTree); + + void visitElseStatement(PyElseStatementTree pyElseStatementTree); + + void visitExecStatement(PyExecStatementTree pyExecStatementTree); + + void visitAssertStatement(PyAssertStatementTree pyAssertStatementTree); + + void visitDelStatement(PyDelStatementTree pyDelStatementTree); + + void visitPassStatement(PyPassStatementTree pyPassStatementTree); + + void visitPrintStatement(PyPrintStatementTree pyPrintStatementTree); + + void visitReturnStatement(PyReturnStatementTree pyReturnStatementTree); + + void visitYieldStatement(PyYieldStatementTree pyYieldStatementTree); + + void visitYieldExpression(PyYieldExpressionTree pyYieldExpressionTree); + + void visitRaiseStatement(PyRaiseStatementTree pyRaiseStatementTree); + + void visitBreakStatement(PyBreakStatementTree pyBreakStatementTree); + + void visitContinueStatement(PyContinueStatementTree pyContinueStatementTree); + + void visitFunctionDef(PyFunctionDefTree pyFunctionDefTree); + + void visitName(PyNameTree pyNameTree); + + void visitClassDef(PyClassDefTree pyClassDefTree); + + void visitAliasedName(PyAliasedNameTree pyAliasedNameTree); + + void visitDottedName(PyDottedNameTree pyDottedNameTree); + + void visitImportFrom(PyImportFromTree pyImportFromTree); + + void visitImportName(PyImportNameTree pyImportNameTree); + + void visitForStatement(PyForStatementTree pyForStatementTree); + + void visitGlobalStatement(PyGlobalStatementTree pyGlobalStatementTree); + + void visitNonlocalStatement(PyNonlocalStatementTree pyNonlocalStatementTree); + + void visitWhileStatement(PyWhileStatementTree pyWhileStatementTree); + + void visitExpressionStatement(PyExpressionStatementTree pyExpressionStatementTree); + + void visitTryStatement(PyTryStatementTree pyTryStatementTree); + + void visitFinallyClause(PyFinallyClauseTree pyFinallyClauseTree); + + void visitExceptClause(PyExceptClauseTree pyExceptClauseTree); + + void visitWithStatement(PyWithStatementTree pyWithStatementTree); + + void visitWithItem(PyWithItemTree pyWithItemTree); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyTryStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyTryStatementTree.java new file mode 100644 index 0000000000..891174472e --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyTryStatementTree.java @@ -0,0 +1,38 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyTryStatementTree extends PyStatementTree { + Token tryKeyword(); + + List exceptClauses(); + + @CheckForNull + PyFinallyClauseTree finallyClause(); + + @CheckForNull + PyElseStatementTree elseClause(); + + List body(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyTypedArgListTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyTypedArgListTree.java new file mode 100644 index 0000000000..1e73a55de1 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyTypedArgListTree.java @@ -0,0 +1,23 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyTypedArgListTree extends Tree { +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyWhileStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyWhileStatementTree.java new file mode 100644 index 0000000000..a3477c8fe0 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyWhileStatementTree.java @@ -0,0 +1,42 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyWhileStatementTree extends PyStatementTree { + Token whileKeyword(); + + PyExpressionTree condition(); + + Token colon(); + + List body(); + + @CheckForNull + Token elseKeyword(); + + @CheckForNull + Token elseColon(); + + List elseBody(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyWithItemTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyWithItemTree.java new file mode 100644 index 0000000000..b8318543e5 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyWithItemTree.java @@ -0,0 +1,36 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import javax.annotation.CheckForNull; + +public interface PyWithItemTree extends Tree { + + PyExpressionTree test(); + + @CheckForNull + Token as(); + + @CheckForNull + PyExpressionTree expression(); + + +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyWithStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyWithStatementTree.java new file mode 100644 index 0000000000..7d543d9883 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyWithStatementTree.java @@ -0,0 +1,38 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyWithStatementTree extends PyStatementTree { + + List withItems(); + + Token colon(); + + List statements(); + + boolean isAsync(); + + @CheckForNull + Token asyncKeyword(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldExpressionTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldExpressionTree.java new file mode 100644 index 0000000000..d82ff95567 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldExpressionTree.java @@ -0,0 +1,33 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface PyYieldExpressionTree extends PyExpressionTree { + Token yieldKeyword(); + + @CheckForNull + Token fromKeyword(); + + List expressions(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldStatementTree.java new file mode 100644 index 0000000000..2369b8b6d7 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyYieldStatementTree.java @@ -0,0 +1,24 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +public interface PyYieldStatementTree extends PyStatementTree { + PyYieldExpressionTree yieldExpression(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java b/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java new file mode 100644 index 0000000000..8b8e0e375f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java @@ -0,0 +1,103 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.AstNode; + +public interface Tree { + + void accept(PyTreeVisitor visitor); + + boolean is(Kind kind); + + AstNode astNode(); + + enum Kind { + ALIASED_NAME(PyAliasedNameTree.class), + + ASSERT_STMT(PyAssertStatementTree.class), + + BREAK_STMT(PyBreakStatementTree.class), + + CLASSDEF(PyClassDefTree.class), + + CONTINUE_STMT(PyContinueStatementTree.class), + + DEL_STMT(PyDelStatementTree.class), + + DOTTED_NAME(PyDottedNameTree.class), + + ELSE_STMT(PyElseStatementTree.class), + + EXCEPT_CLAUSE(PyExceptClauseTree.class), + + EXEC_STMT(PyExecStatementTree.class), + + EXPRESSION_STMT(PyExpressionStatementTree.class), + + FILE_INPUT(PyFileInputTree.class), + + FINALLY_CLAUSE(PyFinallyClauseTree.class), + + FOR_STMT(PyForStatementTree.class), + + FUNCDEF(PyFunctionDefTree.class), + + GLOBAL_STMT(PyGlobalStatementTree.class), + + IF_STMT(PyIfStatementTree.class), + + IMPORT_FROM(PyImportFromTree.class), + + IMPORT_NAME(PyDottedNameTree.class), + + IMPORT_STMT(PyDottedNameTree.class), + + NAME(PyNameTree.class), + + NONLOCAL_STMT(PyNonlocalStatementTree.class), + + PASS_STMT(PyPassStatementTree.class), + + PRINT_STMT(PyPrintStatementTree.class), + + RAISE_STMT(PyRaiseStatementTree.class), + + RETURN_STMT(PyReturnStatementTree.class), + + TRY_STMT(PyTryStatementTree.class), + + WHILE_STMT(PyWhileStatementTree.class), + + WITH_ITEM(PyWithItemTree.class), + + WITH_STMT(PyWithStatementTree.class), + + YIELD_EXPR(PyYieldExpressionTree.class), + + YIELD_STMT(PyYieldStatementTree.class); + + final Class associatedInterface; + + Kind(Class associatedInterface) { + this.associatedInterface = associatedInterface; + } + } +} diff --git a/python-squid/src/main/java/org/sonar/python/metrics/FileMetrics.java b/python-squid/src/main/java/org/sonar/python/metrics/FileMetrics.java index e44bcc9b1c..238f222677 100644 --- a/python-squid/src/main/java/org/sonar/python/metrics/FileMetrics.java +++ b/python-squid/src/main/java/org/sonar/python/metrics/FileMetrics.java @@ -35,7 +35,7 @@ public class FileMetrics { private List functionComplexities = new ArrayList<>(); public FileMetrics(PythonVisitorContext context, boolean ignoreHeaderComments) { - AstNode rootTree = context.rootTree(); + AstNode rootTree = context.rootAstNode(); numberOfStatements = rootTree.getDescendants(PythonGrammar.STATEMENT).size(); numberOfClasses = rootTree.getDescendants(PythonGrammar.CLASSDEF).size(); complexityVisitor.scanFile(context); diff --git a/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java b/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java new file mode 100644 index 0000000000..569eab8d88 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java @@ -0,0 +1,257 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import java.util.List; +import javax.annotation.Nullable; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyAssertStatementTree; +import org.sonar.python.api.tree.PyBreakStatementTree; +import org.sonar.python.api.tree.PyClassDefTree; +import org.sonar.python.api.tree.PyContinueStatementTree; +import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyDottedNameTree; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyExceptClauseTree; +import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyExpressionStatementTree; +import org.sonar.python.api.tree.PyFileInputTree; +import org.sonar.python.api.tree.PyFinallyClauseTree; +import org.sonar.python.api.tree.PyForStatementTree; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyGlobalStatementTree; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyImportFromTree; +import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyNonlocalStatementTree; +import org.sonar.python.api.tree.PyPassStatementTree; +import org.sonar.python.api.tree.PyPrintStatementTree; +import org.sonar.python.api.tree.PyRaiseStatementTree; +import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyTryStatementTree; +import org.sonar.python.api.tree.PyWhileStatementTree; +import org.sonar.python.api.tree.PyWithItemTree; +import org.sonar.python.api.tree.PyWithStatementTree; +import org.sonar.python.api.tree.PyYieldExpressionTree; +import org.sonar.python.api.tree.PyYieldStatementTree; +import org.sonar.python.api.tree.Tree; + +/** + * Default implementation of {@link org.sonar.python.api.tree.PyTreeVisitor}. + */ +public class BaseTreeVisitor implements PyTreeVisitor { + + protected void scan(@Nullable Tree tree) { + if (tree != null) { + tree.accept(this); + } + } + + protected void scan(List trees) { + if (trees != null) { + for (Tree tree : trees) { + scan(tree); + } + } + } + + @Override + public void visitFileInput(PyFileInputTree pyFileInputTree) { + scan(pyFileInputTree.statements()); + } + + @Override + public void visitIfStatement(PyIfStatementTree pyIfStatementTree) { + scan(pyIfStatementTree.condition()); + scan(pyIfStatementTree.body()); + scan(pyIfStatementTree.elifBranches()); + scan(pyIfStatementTree.elseBranch()); + } + + @Override + public void visitElseStatement(PyElseStatementTree pyElseStatementTree) { + scan(pyElseStatementTree.body()); + } + + @Override + public void visitExecStatement(PyExecStatementTree pyExecStatementTree) { + scan(pyExecStatementTree.expression()); + scan(pyExecStatementTree.globalsExpression()); + scan(pyExecStatementTree.localsExpression()); + } + + @Override + public void visitAssertStatement(PyAssertStatementTree pyAssertStatementTree) { + scan(pyAssertStatementTree.expressions()); + } + + @Override + public void visitDelStatement(PyDelStatementTree pyDelStatementTree) { + scan(pyDelStatementTree.expressions()); + } + + @Override + public void visitPassStatement(PyPassStatementTree pyPassStatementTree) { + // nothing to visit for pass statement + } + + @Override + public void visitPrintStatement(PyPrintStatementTree pyPrintStatementTree) { + scan(pyPrintStatementTree.expressions()); + } + + @Override + public void visitReturnStatement(PyReturnStatementTree pyReturnStatementTree) { + scan(pyReturnStatementTree.expressions()); + } + + @Override + public void visitYieldStatement(PyYieldStatementTree pyYieldStatementTree) { + scan(pyYieldStatementTree.yieldExpression()); + } + + @Override + public void visitYieldExpression(PyYieldExpressionTree pyYieldExpressionTree) { + scan(pyYieldExpressionTree.expressions()); + } + + @Override + public void visitRaiseStatement(PyRaiseStatementTree pyRaiseStatementTree) { + scan(pyRaiseStatementTree.expressions()); + scan(pyRaiseStatementTree.fromExpression()); + } + + @Override + public void visitBreakStatement(PyBreakStatementTree pyBreakStatementTree) { + // nothing to visit for break statement + } + + @Override + public void visitContinueStatement(PyContinueStatementTree pyContinueStatementTree) { + // nothing to visit for continue statement + } + + @Override + public void visitFunctionDef(PyFunctionDefTree pyFunctionDefTree) { + scan(pyFunctionDefTree.decorators()); + scan(pyFunctionDefTree.name()); + scan(pyFunctionDefTree.typedArgs()); + scan(pyFunctionDefTree.annotationReturn()); + scan(pyFunctionDefTree.body()); + } + + @Override + public void visitName(PyNameTree pyNameTree) { + // nothing to scan on a name + } + + @Override + public void visitClassDef(PyClassDefTree pyClassDefTree) { + scan(pyClassDefTree.name()); + scan(pyClassDefTree.args()); + scan(pyClassDefTree.body()); + } + + @Override + public void visitAliasedName(PyAliasedNameTree pyAliasedNameTree) { + scan(pyAliasedNameTree.dottedName()); + scan(pyAliasedNameTree.alias()); + } + + @Override + public void visitDottedName(PyDottedNameTree pyDottedNameTree) { + scan(pyDottedNameTree.names()); + } + + @Override + public void visitImportFrom(PyImportFromTree pyImportFromTree) { + scan(pyImportFromTree.module()); + scan(pyImportFromTree.importedNames()); + } + + @Override + public void visitForStatement(PyForStatementTree pyForStatementTree) { + scan(pyForStatementTree.expressions()); + scan(pyForStatementTree.testExpressions()); + scan(pyForStatementTree.body()); + scan(pyForStatementTree.elseBody()); + } + + @Override + public void visitImportName(PyImportNameTree pyImportNameTree) { + scan(pyImportNameTree.modules()); + } + + @Override + public void visitGlobalStatement(PyGlobalStatementTree pyGlobalStatementTree) { + scan(pyGlobalStatementTree.variables()); + } + + @Override + public void visitNonlocalStatement(PyNonlocalStatementTree pyNonlocalStatementTree) { + scan(pyNonlocalStatementTree.variables()); + } + + @Override + public void visitWhileStatement(PyWhileStatementTree pyWhileStatementTree) { + scan(pyWhileStatementTree.condition()); + scan(pyWhileStatementTree.body()); + scan(pyWhileStatementTree.elseBody()); + } + + @Override + public void visitExpressionStatement(PyExpressionStatementTree pyExpressionStatementTree) { + scan(pyExpressionStatementTree.expressions()); + } + + @Override + public void visitTryStatement(PyTryStatementTree pyTryStatementTree) { + scan(pyTryStatementTree.body()); + scan(pyTryStatementTree.exceptClauses()); + scan(pyTryStatementTree.finallyClause()); + scan(pyTryStatementTree.elseClause()); + } + + @Override + public void visitFinallyClause(PyFinallyClauseTree pyFinallyClauseTree) { + scan(pyFinallyClauseTree.body()); + } + + @Override + public void visitExceptClause(PyExceptClauseTree pyExceptClauseTree) { + scan(pyExceptClauseTree.exception()); + scan(pyExceptClauseTree.exceptionInstance()); + scan(pyExceptClauseTree.body()); + } + + @Override + public void visitWithStatement(PyWithStatementTree pyWithStatementTree) { + scan(pyWithStatementTree.withItems()); + scan(pyWithStatementTree.statements()); + } + + @Override + public void visitWithItem(PyWithItemTree pyWithItemTree) { + scan(pyWithItemTree.test()); + scan(pyWithItemTree.expression()); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyAliasedNameTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyAliasedNameTreeImpl.java new file mode 100644 index 0000000000..0c51107eb1 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyAliasedNameTreeImpl.java @@ -0,0 +1,69 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyDottedNameTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyAliasedNameTreeImpl extends PyTree implements PyAliasedNameTree { + + private final Token asKeyword; + private final PyDottedNameTree dottedName; + private final PyNameTree alias; + + public PyAliasedNameTreeImpl(AstNode astNode, Token asKeyword, PyDottedNameTree dottedName, PyNameTree alias) { + super(astNode); + this.asKeyword = asKeyword; + this.dottedName = dottedName; + this.alias = alias; + } + + @CheckForNull + @Override + public Token asKeyword() { + return asKeyword; + } + + @CheckForNull + @Override + public PyNameTree alias() { + return alias; + } + + @Override + public PyDottedNameTree dottedName() { + return dottedName; + } + + @Override + public Kind getKind() { + return Kind.ALIASED_NAME; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitAliasedName(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyAssertStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyAssertStatementTreeImpl.java new file mode 100644 index 0000000000..6332d89fa8 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyAssertStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyAssertStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyAssertStatementTreeImpl extends PyTree implements PyAssertStatementTree { + private final Token assertKeyword; + private final List expressions; + + public PyAssertStatementTreeImpl(AstNode astNode, Token assertKeyword, List expressions) { + super(astNode); + this.assertKeyword = assertKeyword; + this.expressions = expressions; + } + + @Override + public Token assertKeyword() { + return assertKeyword; + } + + @Override + public List expressions() { + return expressions; + } + + @Override + public Kind getKind() { + return Kind.ASSERT_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitAssertStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyBreakStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyBreakStatementTreeImpl.java new file mode 100644 index 0000000000..a4bc59a1f1 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyBreakStatementTreeImpl.java @@ -0,0 +1,49 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import org.sonar.python.api.tree.PyBreakStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyBreakStatementTreeImpl extends PyTree implements PyBreakStatementTree { + private final Token breakKeyword; + + public PyBreakStatementTreeImpl(AstNode astNode, Token breakKeyword) { + super(astNode); + this.breakKeyword = breakKeyword; + } + + @Override + public Token breakKeyword() { + return breakKeyword; + } + + @Override + public Kind getKind() { + return Kind.BREAK_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitBreakStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyClassDefTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyClassDefTreeImpl.java new file mode 100644 index 0000000000..69a4373e08 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyClassDefTreeImpl.java @@ -0,0 +1,98 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyArgListTree; +import org.sonar.python.api.tree.PyClassDefTree; +import org.sonar.python.api.tree.PyDecoratorTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyClassDefTreeImpl extends PyTree implements PyClassDefTree { + + private final PyNameTree name; + private final PyArgListTree args; + private final List body; + + public PyClassDefTreeImpl(AstNode astNode, PyNameTree name, PyArgListTree args, List body) { + super(astNode); + this.name = name; + this.args = args; + this.body = body; + } + + @Override + public Kind getKind() { + return Kind.CLASSDEF; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitClassDef(this); + } + + @Override + public List decorators() { + return null; + } + + @Override + public Token classKeyword() { + return null; + } + + @Override + public PyNameTree name() { + return name; + } + + @CheckForNull + @Override + public Token leftPar() { + return null; + } + + @CheckForNull + @Override + public PyArgListTree args() { + return args; + } + + @CheckForNull + @Override + public Token rightPar() { + return null; + } + + @Override + public Token colon() { + return null; + } + + @Override + public List body() { + return body; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyContinueStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyContinueStatementTreeImpl.java new file mode 100644 index 0000000000..5f0bcf9ff6 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyContinueStatementTreeImpl.java @@ -0,0 +1,49 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import org.sonar.python.api.tree.PyContinueStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyContinueStatementTreeImpl extends PyTree implements PyContinueStatementTree { + private final Token continueKeyword; + + public PyContinueStatementTreeImpl(AstNode astNode, Token continueKeyword) { + super(astNode); + this.continueKeyword = continueKeyword; + } + + @Override + public Token continueKeyword() { + return continueKeyword; + } + + @Override + public Kind getKind() { + return Kind.CONTINUE_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitContinueStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyDelStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyDelStatementTreeImpl.java new file mode 100644 index 0000000000..1079aba3b3 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyDelStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyDelStatementTreeImpl extends PyTree implements PyDelStatementTree { + private final Token delKeyword; + private final List expressionTrees; + + public PyDelStatementTreeImpl(AstNode astNode, Token delKeyword, List expressionTrees) { + super(astNode); + this.delKeyword = delKeyword; + this.expressionTrees = expressionTrees; + } + + @Override + public Token delKeyword() { + return delKeyword; + } + + @Override + public List expressions() { + return expressionTrees; + } + + @Override + public Kind getKind() { + return Kind.DEL_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitDelStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyDottedNameTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyDottedNameTreeImpl.java new file mode 100644 index 0000000000..290993f3f1 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyDottedNameTreeImpl.java @@ -0,0 +1,50 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import java.util.List; +import org.sonar.python.api.tree.PyDottedNameTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyDottedNameTreeImpl extends PyTree implements PyDottedNameTree { + private final List names; + + public PyDottedNameTreeImpl(AstNode astNode, List names) { + super(astNode); + this.names = names; + } + + @Override + public List names() { + return names; + } + + @Override + public Kind getKind() { + return Kind.DOTTED_NAME; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitDottedName(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyElseStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyElseStatementTreeImpl.java new file mode 100644 index 0000000000..cd9abcdb40 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyElseStatementTreeImpl.java @@ -0,0 +1,59 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PyElseStatementTreeImpl extends PyTree implements PyElseStatementTree { + private final Token elseKeyword; + private final List body; + + public PyElseStatementTreeImpl(AstNode astNode, Token elseKeyword, List body) { + super(astNode); + this.elseKeyword = elseKeyword; + this.body = body; + } + + @Override + public Kind getKind() { + return Tree.Kind.ELSE_STMT; + } + + @Override + public Token elseKeyword() { + return elseKeyword; + } + + @Override + public List body() { + return body; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitElseStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyExceptClauseTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyExceptClauseTreeImpl.java new file mode 100644 index 0000000000..d06d44beb0 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyExceptClauseTreeImpl.java @@ -0,0 +1,112 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyExceptClauseTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyExceptClauseTreeImpl extends PyTree implements PyExceptClauseTree { + private final Token exceptKeyword; + private final List body; + private final PyExpressionTree exception; + private final Token asKeyword; + private final Token commaToken; + private final PyExpressionTree exceptionInstance; + + public PyExceptClauseTreeImpl(AstNode astNode, Token exceptKeyword, List body) { + super(astNode); + this.exceptKeyword = exceptKeyword; + this.body = body; + this.exception = null; + this.asKeyword = null; + this.commaToken = null; + this.exceptionInstance = null; + } + + public PyExceptClauseTreeImpl(AstNode astNode, Token exceptKeyword, List body, PyExpressionTree exception, AstNode asNode, AstNode commaNode, PyExpressionTree exceptionInstance) { + super(astNode); + this.exceptKeyword = exceptKeyword; + this.body = body; + this.exception = exception; + this.asKeyword = asNode != null ? asNode.getToken() : null; + this.commaToken = commaNode != null ? commaNode.getToken() : null; + this.exceptionInstance = exceptionInstance; + } + + public PyExceptClauseTreeImpl(AstNode except, Token exceptKeyword, List body, PyExpressionTree exception) { + super(except); + this.exceptKeyword = exceptKeyword; + this.body = body; + this.exception = exception; + this.asKeyword = null; + this.commaToken = null; + this.exceptionInstance = null; + } + + @Override + public Token exceptKeyword() { + return exceptKeyword; + } + + @Override + public List body() { + return body; + } + + @CheckForNull + @Override + public Token asKeyword() { + return asKeyword; + } + + @CheckForNull + @Override + public Token commaToken() { + return commaToken; + } + + @CheckForNull + @Override + public PyExpressionTree exception() { + return exception; + } + + @CheckForNull + @Override + public PyExpressionTree exceptionInstance() { + return exceptionInstance; + } + + @Override + public Kind getKind() { + return Kind.EXCEPT_CLAUSE; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitExceptClause(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyExecStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyExecStatementTreeImpl.java new file mode 100644 index 0000000000..f2705b403f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyExecStatementTreeImpl.java @@ -0,0 +1,79 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyExecStatementTreeImpl extends PyTree implements PyExecStatementTree { + private final Token execKeyword; + private final PyExpressionTree expression; + private final PyExpressionTree globalsExpression; + private final PyExpressionTree localsExpression; + + public PyExecStatementTreeImpl(AstNode astNode, Token execKeyword, PyExpressionTree expression, PyExpressionTree globalsExpression, PyExpressionTree localsExpression) { + super(astNode); + this.execKeyword = execKeyword; + this.expression = expression; + this.globalsExpression = globalsExpression; + this.localsExpression = localsExpression; + } + + public PyExecStatementTreeImpl(AstNode astNode, Token execKeyword, PyExpressionTree expression) { + super(astNode); + this.execKeyword = execKeyword; + this.expression = expression; + globalsExpression = null; + localsExpression = null; + } + + @Override + public Token execKeyword() { + return execKeyword; + } + + @Override + public PyExpressionTree expression() { + return expression; + } + + @Override + public PyExpressionTree globalsExpression() { + return globalsExpression; + } + + @Override + public PyExpressionTree localsExpression() { + return localsExpression; + } + + @Override + public Kind getKind() { + return Kind.EXEC_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitExecStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyExpressionStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyExpressionStatementTreeImpl.java new file mode 100644 index 0000000000..c4789be376 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyExpressionStatementTreeImpl.java @@ -0,0 +1,49 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import java.util.List; +import org.sonar.python.api.tree.PyExpressionStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyExpressionStatementTreeImpl extends PyTree implements PyExpressionStatementTree { + private final List expressions; + + public PyExpressionStatementTreeImpl(AstNode astNode, List expressions) { + super(astNode); + this.expressions = expressions; + } + @Override + public Kind getKind() { + return Kind.EXPRESSION_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitExpressionStatement(this); + } + + @Override + public List expressions() { + return expressions; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyExpressionTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyExpressionTreeImpl.java new file mode 100644 index 0000000000..9cee6fab0e --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyExpressionTreeImpl.java @@ -0,0 +1,41 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +// FIXME : this class is a placeholder while we implement concrete type of expressions and should be deleted. +public class PyExpressionTreeImpl extends PyTree implements PyExpressionTree { + public PyExpressionTreeImpl(AstNode astNode) { + super(astNode); + } + + @Override + public Kind getKind() { + return null; + } + + @Override + public void accept(PyTreeVisitor visitor) { + //TODO : remove + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyFileInputTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyFileInputTreeImpl.java new file mode 100644 index 0000000000..2afb9ffd3f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyFileInputTreeImpl.java @@ -0,0 +1,52 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import java.util.List; +import org.sonar.python.api.tree.PyFileInputTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PyFileInputTreeImpl extends PyTree implements PyFileInputTree { + + private final List statements; + + public PyFileInputTreeImpl(AstNode astNode, List statements) { + super(astNode); + this.statements = statements; + } + + @Override + public Kind getKind() { + return Tree.Kind.FILE_INPUT; + } + + @Override + public List statements() { + return statements; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitFileInput(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyFinallyClauseTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyFinallyClauseTreeImpl.java new file mode 100644 index 0000000000..33fa2c585f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyFinallyClauseTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyFinallyClauseTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyFinallyClauseTreeImpl extends PyTree implements PyFinallyClauseTree { + private final Token finallyKeyword; + private final List body; + + public PyFinallyClauseTreeImpl(AstNode astNode, Token finallyKeyword, List body) { + super(astNode); + this.finallyKeyword = finallyKeyword; + this.body = body; + } + + @Override + public Token finallyKeyword() { + return finallyKeyword; + } + + @Override + public List body() { + return body; + } + + @Override + public Kind getKind() { + return Kind.FINALLY_CLAUSE; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitFinallyClause(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyForStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyForStatementTreeImpl.java new file mode 100644 index 0000000000..c87d5304f6 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyForStatementTreeImpl.java @@ -0,0 +1,118 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyForStatementTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyForStatementTreeImpl extends PyTree implements PyForStatementTree { + + private final List expressions; + private final List testExpressions; + private final List body; + private final List elseBody; + private final Token asyncKeyword; + private final boolean isAsync; + + public PyForStatementTreeImpl(AstNode astNode, List expressions, List testExpressions, List body, List elseBody, Token asyncKeyword) { + super(astNode); + this.expressions = expressions; + this.testExpressions = testExpressions; + this.body = body; + this.elseBody = elseBody; + this.asyncKeyword = asyncKeyword; + this.isAsync = asyncKeyword != null; + } + + @Override + public Kind getKind() { + return Kind.FOR_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitForStatement(this); + } + + @Override + public Token forKeyword() { + return null; + } + + @Override + public List expressions() { + return expressions; + } + + @Override + public Token inKeyword() { + return null; + } + + @Override + public List testExpressions() { + return testExpressions; + } + + @Override + public Token colon() { + return null; + } + + @Override + public List body() { + return body; + } + + @CheckForNull + @Override + public Token elseKeyword() { + return null; + } + + @CheckForNull + @Override + public Token elseColon() { + return null; + } + + @CheckForNull + @Override + public List elseBody() { + return elseBody; + } + + @Override + public boolean isAsync() { + return isAsync; + } + + @CheckForNull + @Override + public Token asyncKeyword() { + return asyncKeyword; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyFunctionDefTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyFunctionDefTreeImpl.java new file mode 100644 index 0000000000..9110e1e87f --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyFunctionDefTreeImpl.java @@ -0,0 +1,120 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyDecoratorTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyTypedArgListTree; + +public class PyFunctionDefTreeImpl extends PyTree implements PyFunctionDefTree { + + private final PyNameTree name; + private final PyTypedArgListTree typedArgs; + private final List body; + + public PyFunctionDefTreeImpl(AstNode astNode, PyNameTree name, PyTypedArgListTree typedArgs, List body) { + super(astNode); + this.name = name; + this.typedArgs = typedArgs; + this.body = body; + } + + @Override + public List decorators() { + return null; + } + + @Override + public Token defKeyword() { + return null; + } + + @CheckForNull + @Override + public Token asyncKeyword() { + return null; + } + + @Override + public PyNameTree name() { + return name; + } + + @Override + public Token leftPar() { + return null; + } + + @Override + public PyTypedArgListTree typedArgs() { + return typedArgs; + } + + @Override + public Token rightPar() { + return null; + } + + @CheckForNull + @Override + public Token dash() { + return null; + } + + @CheckForNull + @Override + public Token gt() { + return null; + } + + @CheckForNull + @Override + public PyExpressionTree annotationReturn() { + return null; + } + + @Override + public Token colon() { + return null; + } + + @Override + public List body() { + return body; + } + + @Override + public Kind getKind() { + return Kind.FUNCDEF; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitFunctionDef(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyGlobalStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyGlobalStatementTreeImpl.java new file mode 100644 index 0000000000..58b1004326 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyGlobalStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyGlobalStatementTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyGlobalStatementTreeImpl extends PyTree implements PyGlobalStatementTree { + private final Token globalKeyword; + private final List variables; + + public PyGlobalStatementTreeImpl(AstNode astNode, Token globalKeyword, List variables) { + super(astNode); + this.globalKeyword = globalKeyword; + this.variables = variables; + } + + @Override + public Token globalKeyword() { + return globalKeyword; + } + + @Override + public List variables() { + return variables; + } + + @Override + public Kind getKind() { + return Kind.GLOBAL_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitGlobalStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyIfStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyIfStatementTreeImpl.java new file mode 100644 index 0000000000..675d67a3cf --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyIfStatementTreeImpl.java @@ -0,0 +1,111 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.Tree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyIfStatementTreeImpl extends PyTree implements PyIfStatementTree { + + private final Token keyword; + private final PyExpressionTree condition; + private final List statements; + private final List elifBranches; + private final boolean isElif; + @CheckForNull + private final PyElseStatementTree elseStatement; + + /** + * + * If statement constructor + */ + public PyIfStatementTreeImpl(AstNode node, Token ifKeyword, PyExpressionTree condition, List statements, List elifBranches, @CheckForNull PyElseStatementTree elseStatement) { + super(node); + this.keyword = ifKeyword; + this.condition = condition; + this.statements = statements; + this.elifBranches = elifBranches; + this.isElif = false; + this.elseStatement = elseStatement; + } + + /** + * Elif statement constructor + */ + public PyIfStatementTreeImpl(AstNode node, Token elifKeyword, PyExpressionTree condition, List statements) { + super(node); + this.keyword = elifKeyword; + this.condition = condition; + this.statements = statements; + this.elifBranches = Collections.emptyList(); + this.isElif = true; + this.elseStatement = null; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public PyExpressionTree condition() { + return condition; + } + + @Override + public List body() { + return statements; + } + + @Override + public List elifBranches() { + return elifBranches; + } + + @Override + public boolean isElif() { + return isElif; + } + + @CheckForNull + @Override + public PyElseStatementTree elseBranch() { + return elseStatement; + } + + @Override + public Kind getKind() { + return Tree.Kind.IF_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitIfStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyImportFromTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyImportFromTreeImpl.java new file mode 100644 index 0000000000..eed6814828 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyImportFromTreeImpl.java @@ -0,0 +1,100 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.PythonPunctuator; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyDottedNameTree; +import org.sonar.python.api.tree.PyImportFromTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyImportFromTreeImpl extends PyTree implements PyImportFromTree { + private final Token fromKeyword; + private final List dottedPrefixForModule; + private final PyDottedNameTree moduleName; + private final Token importKeyword; + private final List aliasedImportNames; + private final boolean isWildcardImport; + private final Token wildcard; + + public PyImportFromTreeImpl(AstNode astNode, Token fromKeyword, List dottedPrefixForModule, PyDottedNameTree moduleName, Token importKeyword, List aliasedImportNames, boolean isWildcardImport) { + super(astNode); + this.fromKeyword = fromKeyword; + this.dottedPrefixForModule = dottedPrefixForModule; + this.moduleName = moduleName; + this.importKeyword = importKeyword; + this.aliasedImportNames = aliasedImportNames; + this.isWildcardImport = isWildcardImport; + this.wildcard = isWildcardImport ? astNode.getFirstChild(PythonPunctuator.MUL).getToken() : null; + } + + @Override + public Token fromKeyword() { + return fromKeyword; + } + + @CheckForNull + @Override + public PyDottedNameTree module() { + return moduleName; + } + + @Override + public Token importKeyword() { + return importKeyword; + } + + @CheckForNull + @Override + public List dottedPrefixForModule() { + return dottedPrefixForModule; + } + + @CheckForNull + @Override + public List importedNames() { + return aliasedImportNames; + } + + @Override + public boolean isWildcardImport() { + return isWildcardImport; + } + + @CheckForNull + @Override + public Token wildcard() { + return wildcard; + } + + @Override + public Kind getKind() { + return Kind.IMPORT_FROM; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitImportFrom(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyImportNameTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyImportNameTreeImpl.java new file mode 100644 index 0000000000..b4d4622b20 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyImportNameTreeImpl.java @@ -0,0 +1,59 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyImportNameTreeImpl extends PyTree implements PyImportNameTree { + + private final Token importKeyword; + private final List aliasedNames; + + public PyImportNameTreeImpl(AstNode astNode, Token importKeyword, java.util.List aliasedNames) { + super(astNode); + this.importKeyword = importKeyword; + this.aliasedNames = aliasedNames; + } + + @Override + public Token importKeyword() { + return importKeyword; + } + + @Override + public List modules() { + return aliasedNames; + } + + @Override + public Kind getKind() { + return Kind.IMPORT_NAME; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitImportName(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyNameTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyNameTreeImpl.java new file mode 100644 index 0000000000..4224afaf65 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyNameTreeImpl.java @@ -0,0 +1,48 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyNameTreeImpl extends PyTree implements PyNameTree { + private final String name; + + public PyNameTreeImpl(AstNode astNode, String name) { + super(astNode); + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public Kind getKind() { + return Kind.NAME; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitName(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyNonlocalStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyNonlocalStatementTreeImpl.java new file mode 100644 index 0000000000..5e1e56153a --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyNonlocalStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyNonlocalStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyNonlocalStatementTreeImpl extends PyTree implements PyNonlocalStatementTree { + private final Token nonlocalKeyword; + private final List variables; + + public PyNonlocalStatementTreeImpl(AstNode astNode, Token nonlocalKeyword, List variables) { + super(astNode); + this.nonlocalKeyword = nonlocalKeyword; + this.variables = variables; + } + + @Override + public Token nonlocalKeyword() { + return nonlocalKeyword; + } + + @Override + public List variables() { + return variables; + } + + @Override + public Kind getKind() { + return Kind.NONLOCAL_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitNonlocalStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyPassStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyPassStatementTreeImpl.java new file mode 100644 index 0000000000..dae6437085 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyPassStatementTreeImpl.java @@ -0,0 +1,49 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import org.sonar.python.api.tree.PyPassStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyPassStatementTreeImpl extends PyTree implements PyPassStatementTree { + private final Token passKeyword; + + public PyPassStatementTreeImpl(AstNode astNode, Token passKeyword) { + super(astNode); + this.passKeyword = passKeyword; + } + + @Override + public Token passKeyword() { + return passKeyword; + } + + @Override + public Kind getKind() { + return Kind.PASS_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitPassStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyPrintStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyPrintStatementTreeImpl.java new file mode 100644 index 0000000000..e6843c80f4 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyPrintStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyPrintStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyPrintStatementTreeImpl extends PyTree implements PyPrintStatementTree { + private final Token printKeyword; + private final List expressions; + + public PyPrintStatementTreeImpl(AstNode astNode, Token printKeyword, List expressions) { + super(astNode); + this.printKeyword = printKeyword; + this.expressions = expressions; + } + + @Override + public Token printKeyword() { + return printKeyword; + } + + @Override + public List expressions() { + return expressions; + } + + @Override + public Kind getKind() { + return Kind.PRINT_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitPrintStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyRaiseStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyRaiseStatementTreeImpl.java new file mode 100644 index 0000000000..4a3bdfdf68 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyRaiseStatementTreeImpl.java @@ -0,0 +1,75 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyRaiseStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyRaiseStatementTreeImpl extends PyTree implements PyRaiseStatementTree { + private final Token raiseKeyword; + private final List expressions; + private final Token fromKeyword; + private final PyExpressionTree fromExpression; + + public PyRaiseStatementTreeImpl(AstNode astNode, Token raiseKeyword, List expressions, Token fromKeyword, PyExpressionTree fromExpression) { + super(astNode); + this.raiseKeyword = raiseKeyword; + this.expressions = expressions; + this.fromKeyword = fromKeyword; + this.fromExpression = fromExpression; + } + + @Override + public Token raiseKeyword() { + return raiseKeyword; + } + + @CheckForNull + @Override + public Token fromKeyword() { + return fromKeyword; + } + + @CheckForNull + @Override + public PyExpressionTree fromExpression() { + return fromExpression; + } + + @Override + public List expressions() { + return expressions; + } + + @Override + public Kind getKind() { + return Kind.RAISE_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitRaiseStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyReturnStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyReturnStatementTreeImpl.java new file mode 100644 index 0000000000..930b7321dd --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyReturnStatementTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; + +public class PyReturnStatementTreeImpl extends PyTree implements PyReturnStatementTree { + private final Token returnKeyword; + private final List expressionTrees; + + public PyReturnStatementTreeImpl(AstNode astNode, Token returnKeyword, List expressionTrees) { + super(astNode); + this.returnKeyword = returnKeyword; + this.expressionTrees = expressionTrees; + } + + @Override + public Token returnKeyword() { + return returnKeyword; + } + + @Override + public List expressions() { + return expressionTrees; + } + + @Override + public Kind getKind() { + return Kind.RETURN_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitReturnStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyTree.java b/python-squid/src/main/java/org/sonar/python/tree/PyTree.java new file mode 100644 index 0000000000..236c48fb37 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyTree.java @@ -0,0 +1,44 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import org.sonar.python.api.tree.Tree; + +public abstract class PyTree extends AstNode implements Tree { + private final AstNode node; + + public PyTree(AstNode node) { + super(node.getType(), node.getName(), node.getToken()); + this.node = node; + } + + public abstract Kind getKind(); + + @Override + public boolean is(Kind kind) { + return kind == getKind(); + } + + @Override + public AstNode astNode() { + return node; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyTryStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyTryStatementTreeImpl.java new file mode 100644 index 0000000000..b11c8dea76 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyTryStatementTreeImpl.java @@ -0,0 +1,85 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyExceptClauseTree; +import org.sonar.python.api.tree.PyFinallyClauseTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyTryStatementTree; + +public class PyTryStatementTreeImpl extends PyTree implements PyTryStatementTree { + private final Token tryKeyword; + private final List tryBody; + private final List exceptClauses; + private final PyFinallyClauseTree finallyClause; + private final PyElseStatementTree elseStatement; + + public PyTryStatementTreeImpl(AstNode astNode, Token tryKeyword, List tryBody, List exceptClauses, PyFinallyClauseTree finallyClause, PyElseStatementTree elseStatement) { + super(astNode); + this.tryKeyword = tryKeyword; + this.tryBody = tryBody; + this.exceptClauses = exceptClauses; + this.finallyClause = finallyClause; + this.elseStatement = elseStatement; + } + + @Override + public Token tryKeyword() { + return tryKeyword; + } + + @Override + public List exceptClauses() { + return exceptClauses; + } + + @CheckForNull + @Override + public PyFinallyClauseTree finallyClause() { + return finallyClause; + } + + @CheckForNull + @Override + public PyElseStatementTree elseClause() { + return elseStatement; + } + + @Override + public List body() { + return tryBody; + } + + @Override + public Kind getKind() { + return Kind.TRY_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitTryStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyWhileStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyWhileStatementTreeImpl.java new file mode 100644 index 0000000000..56617cb862 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyWhileStatementTreeImpl.java @@ -0,0 +1,91 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyWhileStatementTree; + +public class PyWhileStatementTreeImpl extends PyTree implements PyWhileStatementTree { + + private final PyExpressionTree condition; + private final List body; + private final List elseBody; + + public PyWhileStatementTreeImpl(AstNode astNode, PyExpressionTree condition, List body, List elseBody) { + super(astNode); + this.condition = condition; + this.body = body; + this.elseBody = elseBody; + } + + @Override + public Kind getKind() { + return Kind.WHILE_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitWhileStatement(this); + } + + @Override + public Token whileKeyword() { + return null; + } + + @Override + public PyExpressionTree condition() { + return condition; + } + + @Override + public Token colon() { + return null; + } + + @Override + public List body() { + return body; + } + + @CheckForNull + @Override + public Token elseKeyword() { + return null; + } + + @CheckForNull + @Override + public Token elseColon() { + return null; + } + + @CheckForNull + @Override + public List elseBody() { + return elseBody; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyWithStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyWithStatementTreeImpl.java new file mode 100644 index 0000000000..c02afc1b46 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyWithStatementTreeImpl.java @@ -0,0 +1,126 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyWithItemTree; +import org.sonar.python.api.tree.PyWithStatementTree; + +public class PyWithStatementTreeImpl extends PyTree implements PyWithStatementTree { + + private final List withItems; + private final List statements; + private final Token asyncKeyword; + private final boolean isAsync; + private final Token colon; + + public PyWithStatementTreeImpl(AstNode node, List withItems, Token colon, List statements, Token asyncKeyword) { + super(node); + this.withItems = withItems; + this.colon = colon; + this.statements = statements; + this.asyncKeyword = asyncKeyword; + this.isAsync = asyncKeyword != null; + } + + @Override + public List withItems() { + return withItems; + } + + @Override + public Token colon() { + return colon; + } + + @Override + public List statements() { + return statements; + } + + @Override + public boolean isAsync() { + return isAsync; + } + + @CheckForNull + @Override + public Token asyncKeyword() { + return asyncKeyword; + } + + @Override + public Kind getKind() { + return Kind.WITH_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitWithStatement(this); + } + + public static class PyWithItemTreeImpl extends PyTree implements PyWithItemTree { + + private final PyExpressionTree test; + private final Token as; + private final PyExpressionTree expr; + + public PyWithItemTreeImpl(AstNode node, PyExpressionTree test, @Nullable Token as, @Nullable PyExpressionTree expr) { + super(node); + this.test = test; + this.as = as; + this.expr = expr; + } + + @Override + public PyExpressionTree test() { + return test; + } + + @CheckForNull + @Override + public Token as() { + return as; + } + + @CheckForNull + @Override + public PyExpressionTree expression() { + return expr; + } + + @Override + public Kind getKind() { + return Kind.WITH_ITEM; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitWithItem(this); + } + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyYieldExpressionTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyYieldExpressionTreeImpl.java new file mode 100644 index 0000000000..6ead7ba67d --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyYieldExpressionTreeImpl.java @@ -0,0 +1,67 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyYieldExpressionTree; + +public class PyYieldExpressionTreeImpl extends PyTree implements PyYieldExpressionTree { + private final Token yieldKeyword; + private final Token fromKeyword; + private final List expressionTrees; + + public PyYieldExpressionTreeImpl(AstNode astNode, Token yieldKeyword, Token fromKeyword, List expressionTrees) { + super(astNode); + this.yieldKeyword = yieldKeyword; + this.fromKeyword = fromKeyword; + this.expressionTrees = expressionTrees; + } + + @Override + public Token yieldKeyword() { + return yieldKeyword; + } + + @CheckForNull + @Override + public Token fromKeyword() { + return fromKeyword; + } + + @Override + public List expressions() { + return expressionTrees; + } + + @Override + public Kind getKind() { + return Kind.YIELD_EXPR; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitYieldExpression(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyYieldStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyYieldStatementTreeImpl.java new file mode 100644 index 0000000000..aa86ea8740 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyYieldStatementTreeImpl.java @@ -0,0 +1,49 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.PyYieldExpressionTree; +import org.sonar.python.api.tree.PyYieldStatementTree; + +public class PyYieldStatementTreeImpl extends PyTree implements PyYieldStatementTree { + private final PyYieldExpressionTree yieldExpression; + + public PyYieldStatementTreeImpl(AstNode astNode, PyYieldExpressionTree yieldExpression) { + super(astNode); + this.yieldExpression = yieldExpression; + } + + @Override + public PyYieldExpressionTree yieldExpression() { + return yieldExpression; + } + + @Override + public Kind getKind() { + return Kind.YIELD_STMT; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitYieldStatement(this); + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java b/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java new file mode 100644 index 0000000000..bdf7d16c1c --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java @@ -0,0 +1,520 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import com.sonar.sslr.api.GenericTokenType; +import com.sonar.sslr.api.Token; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.python.api.PythonGrammar; +import org.sonar.python.api.PythonKeyword; +import org.sonar.python.api.PythonPunctuator; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyArgListTree; +import org.sonar.python.api.tree.PyAssertStatementTree; +import org.sonar.python.api.tree.PyBreakStatementTree; +import org.sonar.python.api.tree.PyClassDefTree; +import org.sonar.python.api.tree.PyContinueStatementTree; +import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyDottedNameTree; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyExceptClauseTree; +import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyExpressionStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyFileInputTree; +import org.sonar.python.api.tree.PyFinallyClauseTree; +import org.sonar.python.api.tree.PyForStatementTree; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyGlobalStatementTree; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyImportFromTree; +import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyImportStatementTree; +import org.sonar.python.api.tree.PyNameTree; +import org.sonar.python.api.tree.PyNonlocalStatementTree; +import org.sonar.python.api.tree.PyPassStatementTree; +import org.sonar.python.api.tree.PyPrintStatementTree; +import org.sonar.python.api.tree.PyRaiseStatementTree; +import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PyStatementTree; +import org.sonar.python.api.tree.PyTryStatementTree; +import org.sonar.python.api.tree.PyTypedArgListTree; +import org.sonar.python.api.tree.PyWithItemTree; +import org.sonar.python.api.tree.PyWithStatementTree; +import org.sonar.python.api.tree.PyYieldExpressionTree; +import org.sonar.python.api.tree.PyYieldStatementTree; + +public class PythonTreeMaker { + + public PyFileInputTree fileInput(AstNode astNode) { + List statements = getStatements(astNode).stream().map(this::statement).collect(Collectors.toList()); + return new PyFileInputTreeImpl(astNode, statements); + } + + PyStatementTree statement(AstNode astNode) { + if (astNode.is(PythonGrammar.IF_STMT)) { + return ifStatement(astNode); + } + if (astNode.is(PythonGrammar.PASS_STMT)) { + return passStatement(astNode); + } + if (astNode.is(PythonGrammar.PRINT_STMT)) { + return printStatement(astNode); + } + if (astNode.is(PythonGrammar.EXEC_STMT)) { + return execStatement(astNode); + } + if (astNode.is(PythonGrammar.ASSERT_STMT)) { + return assertStatement(astNode); + } + if (astNode.is(PythonGrammar.PASS_STMT)) { + return passStatement(astNode); + } + if (astNode.is(PythonGrammar.DEL_STMT)) { + return delStatement(astNode); + } + if (astNode.is(PythonGrammar.RETURN_STMT)) { + return returnStatement(astNode); + } + if (astNode.is(PythonGrammar.YIELD_STMT)) { + return yieldStatement(astNode); + } + if (astNode.is(PythonGrammar.RAISE_STMT)) { + return raiseStatement(astNode); + } + if (astNode.is(PythonGrammar.BREAK_STMT)) { + return breakStatement(astNode); + } + if (astNode.is(PythonGrammar.CONTINUE_STMT)) { + return continueStatement(astNode); + } + if (astNode.is(PythonGrammar.FUNCDEF)) { + return funcDefStatement(astNode); + } + if (astNode.is(PythonGrammar.CLASSDEF)) { + return classDefStatement(astNode); + } + if (astNode.is(PythonGrammar.IMPORT_STMT)) { + return importStatement(astNode); + } + if (astNode.is(PythonGrammar.FOR_STMT)) { + return forStatement(astNode); + } + if (astNode.is(PythonGrammar.WHILE_STMT)) { + return whileStatement(astNode); + } + if (astNode.is(PythonGrammar.GLOBAL_STMT)) { + return globalStatement(astNode); + } + if (astNode.is(PythonGrammar.NONLOCAL_STMT)) { + return nonlocalStatement(astNode); + } + if (astNode.is(PythonGrammar.EXPRESSION_STMT)) { + return expressionStatement(astNode); + } + if (astNode.is(PythonGrammar.TRY_STMT)) { + return tryStatement(astNode); + } + if (astNode.is(PythonGrammar.ASYNC_STMT) && astNode.hasDirectChildren(PythonGrammar.FOR_STMT)) { + return forStatement(astNode); + } + if (astNode.is(PythonGrammar.ASYNC_STMT) && astNode.hasDirectChildren(PythonGrammar.WITH_STMT)) { + return withStatement(astNode); + } + if (astNode.is(PythonGrammar.WITH_STMT)) { + return withStatement(astNode); + } + throw new IllegalStateException("Statement " + astNode.getType() + " not correctly translated to strongly typed AST"); + } + + private List getStatementsFromSuite(AstNode astNode) { + if (astNode.is(PythonGrammar.SUITE)) { + List statements = getStatements(astNode); + if (statements.isEmpty()) { + AstNode stmtListNode = astNode.getFirstChild(PythonGrammar.STMT_LIST); + return stmtListNode.getChildren(PythonGrammar.SIMPLE_STMT).stream() + .map(AstNode::getFirstChild) + .map(this::statement) + .collect(Collectors.toList()); + } + return statements.stream().map(this::statement) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + private List getStatements(AstNode astNode) { + List statements = astNode.getChildren(PythonGrammar.STATEMENT); + return statements.stream().flatMap(stmt -> { + if (stmt.hasDirectChildren(PythonGrammar.STMT_LIST)) { + AstNode stmtListNode = stmt.getFirstChild(PythonGrammar.STMT_LIST); + return stmtListNode.getChildren(PythonGrammar.SIMPLE_STMT).stream() + .map(AstNode::getFirstChild); + } + return stmt.getChildren(PythonGrammar.COMPOUND_STMT).stream() + .map(AstNode::getFirstChild); + }).collect(Collectors.toList()); + } + + // Simple statements + + public PyPrintStatementTree printStatement(AstNode astNode) { + List expressions = expressionsFromTest(astNode); + return new PyPrintStatementTreeImpl(astNode, astNode.getTokens().get(0), expressions); + } + + public PyExecStatementTree execStatement(AstNode astNode) { + PyExpressionTree expression = expression(astNode.getFirstChild(PythonGrammar.EXPR)); + List expressions = expressionsFromTest(astNode); + if (expressions.isEmpty()) { + return new PyExecStatementTreeImpl(astNode, astNode.getTokens().get(0), expression); + } + return new PyExecStatementTreeImpl(astNode, astNode.getTokens().get(0), expression, expressions.get(0), expressions.size() == 2 ? expressions.get(1) : null); + } + + public PyAssertStatementTree assertStatement(AstNode astNode) { + List expressions = expressionsFromTest(astNode); + return new PyAssertStatementTreeImpl(astNode, astNode.getTokens().get(0), expressions); + } + + public PyPassStatementTree passStatement(AstNode astNode) { + return new PyPassStatementTreeImpl(astNode, astNode.getTokens().get(0)); + } + + public PyDelStatementTree delStatement(AstNode astNode) { + List expressionTrees = expressionsFromExprList(astNode.getFirstChild(PythonGrammar.EXPRLIST)); + return new PyDelStatementTreeImpl(astNode, astNode.getTokens().get(0), expressionTrees); + } + + public PyReturnStatementTree returnStatement(AstNode astNode) { + AstNode testListNode = astNode.getFirstChild(PythonGrammar.TESTLIST); + List expressionTrees = Collections.emptyList(); + if (testListNode != null) { + expressionTrees = expressionsFromTest(testListNode); + } + return new PyReturnStatementTreeImpl(astNode, astNode.getTokens().get(0), expressionTrees); + } + + public PyYieldStatementTree yieldStatement(AstNode astNode) { + return new PyYieldStatementTreeImpl(astNode, yieldExpression(astNode.getFirstChild(PythonGrammar.YIELD_EXPR))); + } + + public PyYieldExpressionTree yieldExpression(AstNode astNode) { + Token yieldKeyword = astNode.getFirstChild(PythonKeyword.YIELD).getToken(); + AstNode nodeContainingExpression = astNode; + AstNode fromKeyword = astNode.getFirstChild(PythonKeyword.FROM); + if (fromKeyword == null) { + nodeContainingExpression = astNode.getFirstChild(PythonGrammar.TESTLIST); + } + List expressionTrees = Collections.emptyList(); + if (nodeContainingExpression != null) { + expressionTrees = expressionsFromTest(nodeContainingExpression); + } + return new PyYieldExpressionTreeImpl(astNode, yieldKeyword, fromKeyword == null ? null : fromKeyword.getToken(), expressionTrees); + } + + public PyRaiseStatementTree raiseStatement(AstNode astNode) { + AstNode fromKeyword = astNode.getFirstChild(PythonKeyword.FROM); + List expressions = new ArrayList<>(); + AstNode fromExpression = null; + if (fromKeyword != null) { + expressions.add(astNode.getFirstChild(PythonGrammar.TEST)); + fromExpression = astNode.getLastChild(PythonGrammar.TEST); + } else { + expressions = astNode.getChildren(PythonGrammar.TEST); + } + List expressionTrees = expressions.stream() + .map(this::expression) + .collect(Collectors.toList()); + return new PyRaiseStatementTreeImpl(astNode, astNode.getFirstChild(PythonKeyword.RAISE).getToken(), + expressionTrees, fromKeyword == null ? null : fromKeyword.getToken(), fromExpression == null ? null : expression(fromExpression)); + } + + public PyBreakStatementTree breakStatement(AstNode astNode) { + return new PyBreakStatementTreeImpl(astNode, astNode.getToken()); + } + + public PyContinueStatementTree continueStatement(AstNode astNode) { + return new PyContinueStatementTreeImpl(astNode, astNode.getToken()); + } + + public PyImportStatementTree importStatement(AstNode astNode) { + AstNode importStmt = astNode.getFirstChild(); + if (importStmt.is(PythonGrammar.IMPORT_NAME)) { + return importName(importStmt); + } + return importFromStatement(importStmt); + } + + private PyImportNameTree importName(AstNode astNode) { + Token importKeyword = astNode.getFirstChild(PythonKeyword.IMPORT).getToken(); + List aliasedNames = astNode + .getFirstChild(PythonGrammar.DOTTED_AS_NAMES) + .getChildren(PythonGrammar.DOTTED_AS_NAME).stream() + .map(this::aliasedName) + .collect(Collectors.toList()); + return new PyImportNameTreeImpl(astNode, importKeyword, aliasedNames); + } + + public PyImportFromTree importFromStatement(AstNode astNode) { + Token importKeyword = astNode.getFirstChild(PythonKeyword.IMPORT).getToken(); + Token fromKeyword = astNode.getFirstChild(PythonKeyword.FROM).getToken(); + List dottedPrefixForModule = astNode.getChildren(PythonPunctuator.DOT).stream() + .map(AstNode::getToken) + .collect(Collectors.toList()); + AstNode moduleNode = astNode.getFirstChild(PythonGrammar.DOTTED_NAME); + PyDottedNameTree moduleName = null; + if (moduleNode != null) { + moduleName = dottedName(moduleNode); + } + AstNode importAsnames = astNode.getFirstChild(PythonGrammar.IMPORT_AS_NAMES); + List aliasedImportNames = null; + boolean isWildcardImport = true; + if (importAsnames != null) { + aliasedImportNames = importAsnames.getChildren(PythonGrammar.IMPORT_AS_NAME).stream() + .map(this::aliasedName) + .collect(Collectors.toList()); + isWildcardImport = false; + } + return new PyImportFromTreeImpl(astNode, fromKeyword, dottedPrefixForModule, moduleName, importKeyword, aliasedImportNames, isWildcardImport); + } + + private PyAliasedNameTree aliasedName(AstNode astNode) { + AstNode asKeyword = astNode.getFirstChild(PythonKeyword.AS); + PyDottedNameTree dottedName; + if (astNode.is(PythonGrammar.DOTTED_AS_NAME)) { + dottedName = dottedName(astNode.getFirstChild(PythonGrammar.DOTTED_NAME)); + } else { + // astNode is IMPORT_AS_NAME + AstNode importedName = astNode.getFirstChild(PythonGrammar.NAME); + dottedName = new PyDottedNameTreeImpl(astNode, Collections.singletonList(name(importedName))); + } + if (asKeyword == null) { + return new PyAliasedNameTreeImpl(astNode, null, dottedName, null); + } + return new PyAliasedNameTreeImpl(astNode, asKeyword.getToken(), dottedName, name(astNode.getLastChild(PythonGrammar.NAME))); + } + + private PyDottedNameTree dottedName(AstNode astNode) { + List names = astNode + .getChildren(PythonGrammar.NAME).stream() + .map(this::name) + .collect(Collectors.toList()); + return new PyDottedNameTreeImpl(astNode, names); + } + + public PyGlobalStatementTree globalStatement(AstNode astNode) { + Token globalKeyword = astNode.getFirstChild(PythonKeyword.GLOBAL).getToken(); + List variables = astNode.getChildren(PythonGrammar.NAME).stream() + .map(this::name) + .collect(Collectors.toList()); + return new PyGlobalStatementTreeImpl(astNode, globalKeyword, variables); + } + + public PyNonlocalStatementTree nonlocalStatement(AstNode astNode) { + Token nonlocalKeyword = astNode.getFirstChild(PythonKeyword.NONLOCAL).getToken(); + List variables = astNode.getChildren(PythonGrammar.NAME).stream() + .map(this::name) + .collect(Collectors.toList()); + return new PyNonlocalStatementTreeImpl(astNode, nonlocalKeyword, variables); + } + // Compound statements + + public PyIfStatementTree ifStatement(AstNode astNode) { + Token ifToken = astNode.getTokens().get(0); + AstNode condition = astNode.getFirstChild(PythonGrammar.TEST); + AstNode suite = astNode.getFirstChild(PythonGrammar.SUITE); + List statements = getStatementsFromSuite(suite); + AstNode elseSuite = astNode.getLastChild(PythonGrammar.SUITE); + PyElseStatementTree elseStatement = null; + if (elseSuite.getPreviousSibling().getPreviousSibling().is(PythonKeyword.ELSE)) { + elseStatement = elseStatement(elseSuite); + } + List elifBranches = astNode.getChildren(PythonKeyword.ELIF).stream() + .map(this::elifStatement) + .collect(Collectors.toList()); + + return new PyIfStatementTreeImpl( + astNode, ifToken, expression(condition), statements, elifBranches, elseStatement); + } + + private PyIfStatementTree elifStatement(AstNode astNode) { + Token elifToken = astNode.getToken(); + AstNode suite = astNode.getNextSibling().getNextSibling().getNextSibling(); + AstNode condition = astNode.getNextSibling(); + List statements = getStatementsFromSuite(suite); + return new PyIfStatementTreeImpl( + astNode, elifToken, expression(condition), statements); + } + + private PyElseStatementTree elseStatement(AstNode astNode) { + Token elseToken = astNode.getPreviousSibling().getPreviousSibling().getToken(); + List statements = getStatementsFromSuite(astNode); + return new PyElseStatementTreeImpl(astNode, elseToken, statements); + } + + public PyFunctionDefTree funcDefStatement(AstNode astNode) { + // TODO decorators + PyNameTree name = name(astNode.getFirstChild(PythonGrammar.FUNCNAME).getFirstChild(PythonGrammar.NAME)); + // TODO argList + PyTypedArgListTree typedArgs = null; + List body = getStatementsFromSuite(astNode.getFirstChild(PythonGrammar.SUITE)); + return new PyFunctionDefTreeImpl(astNode, name, typedArgs, body); + } + + public PyClassDefTree classDefStatement(AstNode astNode) { + // TODO decorators + PyNameTree name = name(astNode.getFirstChild(PythonGrammar.CLASSNAME).getFirstChild(PythonGrammar.NAME)); + // TODO argList + PyArgListTree args = null; + List body = getStatementsFromSuite(astNode.getFirstChild(PythonGrammar.SUITE)); + return new PyClassDefTreeImpl(astNode, name, args, body); + } + + private PyNameTree name(AstNode astNode) { + return new PyNameTreeImpl(astNode, astNode.getFirstChild(GenericTokenType.IDENTIFIER).getTokenOriginalValue()); + } + + public PyForStatementTree forStatement(AstNode astNode) { + AstNode forStatementNode = astNode; + Token asyncToken = null; + if (astNode.is(PythonGrammar.ASYNC_STMT)) { + asyncToken = astNode.getFirstChild().getToken(); + forStatementNode = astNode.getFirstChild(PythonGrammar.FOR_STMT); + } + List expressions = expressionsFromExprList(forStatementNode.getFirstChild(PythonGrammar.EXPRLIST)); + List testExpressions = expressionsFromTest(forStatementNode.getFirstChild(PythonGrammar.TESTLIST)); + AstNode firstSuite = forStatementNode.getFirstChild(PythonGrammar.SUITE); + List body = getStatementsFromSuite(firstSuite); + AstNode lastSuite = forStatementNode.getLastChild(PythonGrammar.SUITE); + List elseBody = lastSuite == firstSuite ? Collections.emptyList() : getStatementsFromSuite(lastSuite); + return new PyForStatementTreeImpl(forStatementNode, expressions, testExpressions, body, elseBody, asyncToken); + } + + public PyWhileStatementTreeImpl whileStatement(AstNode astNode) { + PyExpressionTree condition = expression(astNode.getFirstChild(PythonGrammar.TEST)); + AstNode firstSuite = astNode.getFirstChild(PythonGrammar.SUITE); + List body = getStatementsFromSuite(firstSuite); + AstNode lastSuite = astNode.getLastChild(PythonGrammar.SUITE); + List elseBody = lastSuite == firstSuite ? Collections.emptyList() : getStatementsFromSuite(lastSuite); + return new PyWhileStatementTreeImpl(astNode, condition, body, elseBody); + } + + public PyExpressionStatementTree expressionStatement(AstNode astNode) { + // TODO: handle ANNASSIGN, b.sequence(AUGASSIGN, b.firstOf(YIELD_EXPR, TESTLIST)), b.zeroOrMore("=", b.firstOf(YIELD_EXPR, TESTLIST_STAR_EXPR) + List expressions = astNode.getFirstChild(PythonGrammar.TESTLIST_STAR_EXPR).getChildren(PythonGrammar.TEST, PythonGrammar.STAR_EXPR).stream() + .map(this::expression) + .collect(Collectors.toList()); + return new PyExpressionStatementTreeImpl(astNode, expressions); + } + + public PyTryStatementTree tryStatement(AstNode astNode) { + Token tryKeyword = astNode.getFirstChild(PythonKeyword.TRY).getToken(); + List tryBody = getStatementsFromSuite(astNode.getFirstChild(PythonGrammar.SUITE)); + List exceptClauseTrees = astNode.getChildren(PythonGrammar.EXCEPT_CLAUSE).stream() + .map(except -> { + AstNode suite = except.getNextSibling().getNextSibling(); + return exceptClause(except, getStatementsFromSuite(suite)); + }) + .collect(Collectors.toList()); + PyFinallyClauseTree finallyClause = null; + AstNode finallyNode = astNode.getFirstChild(PythonKeyword.FINALLY); + if (finallyNode != null) { + AstNode finallySuite = finallyNode.getNextSibling().getNextSibling(); + List body = getStatementsFromSuite(finallySuite); + finallyClause = new PyFinallyClauseTreeImpl(finallySuite, finallyNode.getToken(), body); + } + PyElseStatementTree elseStatementTree = null; + AstNode elseNode = astNode.getFirstChild(PythonKeyword.ELSE); + if (elseNode != null) { + elseStatementTree = elseStatement(elseNode.getNextSibling().getNextSibling()); + } + return new PyTryStatementTreeImpl(astNode, tryKeyword, tryBody, exceptClauseTrees, finallyClause, elseStatementTree); + } + + public PyWithStatementTree withStatement(AstNode astNode) { + AstNode withStmtNode = astNode; + Token asyncKeyword = null; + if (astNode.is(PythonGrammar.ASYNC_STMT)) { + withStmtNode = astNode.getFirstChild(PythonGrammar.WITH_STMT); + asyncKeyword = astNode.getFirstChild().getToken(); + } + List withItems = withItems(withStmtNode.getChildren(PythonGrammar.WITH_ITEM)); + AstNode suite = withStmtNode.getFirstChild(PythonGrammar.SUITE); + Token colon = suite.getPreviousSibling().getToken(); + List statements = getStatementsFromSuite(suite); + return new PyWithStatementTreeImpl(withStmtNode, withItems, colon, statements, asyncKeyword); + } + + private List withItems(List withItems) { + return withItems.stream().map(this::withItem).collect(Collectors.toList()); + } + + private PyWithItemTree withItem(AstNode withItem) { + AstNode testNode = withItem.getFirstChild(PythonGrammar.TEST); + PyExpressionTree test = expression(testNode); + AstNode asNode = testNode.getNextSibling(); + PyExpressionTree expr = null; + Token as = null; + if (asNode != null) { + as = asNode.getToken(); + expr = expression(withItem.getFirstChild(PythonGrammar.EXPR)); + } + return new PyWithStatementTreeImpl.PyWithItemTreeImpl(withItem, test, as, expr); + } + + private PyExceptClauseTree exceptClause(AstNode except, List body) { + Token exceptKeyword = except.getFirstChild(PythonKeyword.EXCEPT).getToken(); + AstNode exceptionNode = except.getFirstChild(PythonGrammar.TEST); + if (exceptionNode == null) { + return new PyExceptClauseTreeImpl(except, exceptKeyword, body); + } + AstNode asNode = except.getFirstChild(PythonKeyword.AS); + AstNode commaNode = except.getFirstChild(PythonPunctuator.COMMA); + if (asNode != null || commaNode != null) { + PyExpressionTree exceptionInstance = expression(except.getLastChild(PythonGrammar.TEST)); + return new PyExceptClauseTreeImpl(except, exceptKeyword, body, expression(exceptionNode), asNode, commaNode, exceptionInstance); + } + return new PyExceptClauseTreeImpl(except, exceptKeyword, body, expression(exceptionNode)); + } + + // expressions + + private List expressionsFromTest(AstNode astNode) { + return astNode.getChildren(PythonGrammar.TEST).stream().map(this::expression).collect(Collectors.toList()); + } + + private List expressionsFromExprList(AstNode firstChild) { + return firstChild + .getChildren(PythonGrammar.EXPR, PythonGrammar.STAR_EXPR) + .stream().map(this::expression).collect(Collectors.toList()); + } + + PyExpressionTree expression(AstNode astNode) { + if (astNode.is(PythonGrammar.YIELD_EXPR)) { + return yieldExpression(astNode); + } + return new PyExpressionTreeImpl(astNode); + } +} diff --git a/python-squid/src/test/java/org/sonar/python/PythonCheckTest.java b/python-squid/src/test/java/org/sonar/python/PythonCheckTest.java index 2c380a5ed5..8761f95a5d 100644 --- a/python-squid/src/test/java/org/sonar/python/PythonCheckTest.java +++ b/python-squid/src/test/java/org/sonar/python/PythonCheckTest.java @@ -36,7 +36,7 @@ public class PythonCheckTest { private static final File FILE = new File("src/test/resources/file.py"); public static final String MESSAGE = "message"; - private static List scanFileForIssues(File file, PythonCheck check) { + private static List scanFileForIssues(File file, PythonCheckAstNode check) { PythonVisitorContext context = TestPythonVisitorRunner.createContext(file); check.scanFile(context); return context.getIssues(); @@ -151,7 +151,7 @@ public void visitFile(AstNode astNode) { assertThat(issue.primaryLocation().endLine()).isEqualTo(3); } - private static class TestPythonCheck extends PythonCheck { + private static class TestPythonCheck extends PythonCheckAstNode { @Override public Set subscribedKinds() { diff --git a/python-squid/src/test/java/org/sonar/python/PythonCheckTreeTest.java b/python-squid/src/test/java/org/sonar/python/PythonCheckTreeTest.java new file mode 100644 index 0000000000..28642cc2e5 --- /dev/null +++ b/python-squid/src/test/java/org/sonar/python/PythonCheckTreeTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python; + +import java.io.File; +import java.util.List; +import org.junit.Test; +import org.sonar.python.PythonCheck.PreciseIssue; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyNameTree; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PythonCheckTreeTest { + + private static final File FILE = new File("src/test/resources/file.py"); + public static final String MESSAGE = "message"; + + private static List scanFileForIssues(File file, PythonCheck check) { + PythonVisitorContext context = TestPythonVisitorRunner.createContext(file); + check.scanFile(context); + return context.getIssues(); + } + + @Test + public void test() { + TestPythonCheck check = new TestPythonCheck (){ + @Override + public void visitFunctionDef(PyFunctionDefTree pyFunctionDefTree) { + super.visitFunctionDef(pyFunctionDefTree); + PyNameTree name = pyFunctionDefTree.name(); + addIssue(name, name.astNode().getTokenValue()); + } + }; + + List issues = scanFileForIssues(FILE, check); + + assertThat(issues).hasSize(2); + PreciseIssue firstIssue = issues.get(0); + + assertThat(firstIssue.cost()).isNull(); + assertThat(firstIssue.secondaryLocations()).isEmpty(); + + IssueLocation primaryLocation = firstIssue.primaryLocation(); + assertThat(primaryLocation.message()).isEqualTo("hello"); + + assertThat(primaryLocation.startLine()).isEqualTo(1); + assertThat(primaryLocation.endLine()).isEqualTo(1); + assertThat(primaryLocation.startLineOffset()).isEqualTo(4); + assertThat(primaryLocation.endLineOffset()).isEqualTo(9); + } + + @Test + public void test_cost() { + TestPythonCheck check = new TestPythonCheck (){ + @Override + public void visitFunctionDef(PyFunctionDefTree pyFunctionDefTree) { + super.visitFunctionDef(pyFunctionDefTree); + PyNameTree name = pyFunctionDefTree.name(); + addIssue(name.astNode().getToken(), MESSAGE).withCost(42); + } + }; + + List issues = scanFileForIssues(FILE, check); + PreciseIssue firstIssue = issues.get(0); + assertThat(firstIssue.cost()).isEqualTo(42); + } + + private static class TestPythonCheck extends PythonCheckTree { + + } +} diff --git a/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java b/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java new file mode 100644 index 0000000000..d20e8e17cc --- /dev/null +++ b/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java @@ -0,0 +1,170 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import java.util.function.Function; +import org.junit.Test; +import org.sonar.python.api.PythonGrammar; +import org.sonar.python.api.tree.PyAssertStatementTree; +import org.sonar.python.api.tree.PyClassDefTree; +import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyForStatementTree; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyImportFromTree; +import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyPassStatementTree; +import org.sonar.python.api.tree.PyPrintStatementTree; +import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PyTryStatementTree; +import org.sonar.python.api.tree.PyWithStatementTree; +import org.sonar.python.api.tree.PyYieldStatementTree; +import org.sonar.python.parser.RuleTest; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public class BaseTreeVisitorTest extends RuleTest { + private final PythonTreeMaker treeMaker = new PythonTreeMaker(); + + @Test + public void if_statement() { + setRootRule(PythonGrammar.IF_STMT); + PyIfStatementTree tree = parse("if p1: print 'a'\nelif p2: return\nelse: yield", treeMaker::ifStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitIfStatement(tree); + verify(visitor).visitIfStatement(tree); + verify(visitor).visitIfStatement(tree.elifBranches().get(0)); + verify(visitor).visitPrintStatement((PyPrintStatementTree) tree.body().get(0)); + verify(visitor).visitReturnStatement((PyReturnStatementTree) tree.elifBranches().get(0).body().get(0)); + verify(visitor).visitYieldStatement((PyYieldStatementTree) tree.elseBranch().body().get(0)); + } + + @Test + public void exec_statement() { + setRootRule(PythonGrammar.EXEC_STMT); + PyExecStatementTree tree = parse("exec 'foo' in globals, locals", treeMaker::execStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitExecStatement(tree); + verify(visitor).scan(tree.expression()); + verify(visitor).scan(tree.globalsExpression()); + verify(visitor).scan(tree.localsExpression()); + } + + @Test + public void assert_statement() { + setRootRule(PythonGrammar.ASSERT_STMT); + PyAssertStatementTree tree = parse("assert x, y", treeMaker::assertStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitAssertStatement(tree); + verify(visitor).scan(tree.expressions()); + } + + @Test + public void delete_statement() { + setRootRule(PythonGrammar.DEL_STMT); + PyDelStatementTree tree = parse("del x", treeMaker::delStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitDelStatement(tree); + verify(visitor).scan(tree.expressions()); + } + + @Test + public void fundef_statement() { + setRootRule(PythonGrammar.FUNCDEF); + PyFunctionDefTree pyFunctionDefTree = parse("def foo(): pass", treeMaker::funcDefStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitFunctionDef(pyFunctionDefTree); + verify(visitor).visitName(pyFunctionDefTree.name()); + verify(visitor).visitPassStatement((PyPassStatementTree) pyFunctionDefTree.body().get(0)); + } + + @Test + public void import_statement() { + setRootRule(PythonGrammar.IMPORT_STMT); + PyImportFromTree tree = (PyImportFromTree) parse("from foo import f as g", treeMaker::importStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitImportFrom(tree); + verify(visitor).visitAliasedName(tree.importedNames().get(0)); + verify(visitor).visitDottedName(tree.module()); + + PyImportNameTree pyTree = (PyImportNameTree) parse("import f as g", treeMaker::importStatement); + visitor = spy(BaseTreeVisitor.class); + visitor.visitImportName(pyTree); + verify(visitor).visitAliasedName(pyTree.modules().get(0)); + } + + @Test + public void for_statement() { + setRootRule(PythonGrammar.FOR_STMT); + PyForStatementTree tree = parse("for foo in bar:pass\nelse: pass", treeMaker::forStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitForStatement(tree); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.body().get(0)); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.elseBody().get(0)); + } + + @Test + public void while_statement() { + setRootRule(PythonGrammar.WHILE_STMT); + PyWhileStatementTreeImpl tree = parse("while foo:\n pass\nelse:\n pass", treeMaker::whileStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitWhileStatement(tree); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.body().get(0)); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.elseBody().get(0)); + } + + @Test + public void try_statement() { + setRootRule(PythonGrammar.TRY_STMT); + PyTryStatementTree tree = parse("try: pass\nexcept Error: pass\nfinally: pass", treeMaker::tryStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitTryStatement(tree); + verify(visitor).visitFinallyClause(tree.finallyClause()); + verify(visitor).visitExceptClause(tree.exceptClauses().get(0)); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.body().get(0)); + } + + @Test + public void with_statement() { + setRootRule(PythonGrammar.WITH_STMT); + PyWithStatementTree tree = parse("with foo as bar, qix : pass", treeMaker::withStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitWithStatement(tree); + verify(visitor).visitWithItem(tree.withItems().get(0)); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.statements().get(0)); + } + + @Test + public void class_statement() { + setRootRule(PythonGrammar.CLASSDEF); + PyClassDefTree tree = parse("class clazz: pass", treeMaker::classDefStatement); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitClassDef(tree); + verify(visitor).visitName(tree.name()); + verify(visitor).visitPassStatement((PyPassStatementTree) tree.body().get(0)); + } + + private T parse(String code, Function func) { + return func.apply(p.parse(code)); + } +} diff --git a/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java new file mode 100644 index 0000000000..dc5fa84cfc --- /dev/null +++ b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java @@ -0,0 +1,730 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.AstNode; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import org.junit.Test; +import org.sonar.python.api.PythonGrammar; +import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyAssertStatementTree; +import org.sonar.python.api.tree.PyBreakStatementTree; +import org.sonar.python.api.tree.PyClassDefTree; +import org.sonar.python.api.tree.PyContinueStatementTree; +import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyElseStatementTree; +import org.sonar.python.api.tree.PyExceptClauseTree; +import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyExpressionStatementTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyFileInputTree; +import org.sonar.python.api.tree.PyForStatementTree; +import org.sonar.python.api.tree.PyFunctionDefTree; +import org.sonar.python.api.tree.PyGlobalStatementTree; +import org.sonar.python.api.tree.PyIfStatementTree; +import org.sonar.python.api.tree.PyImportFromTree; +import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyImportStatementTree; +import org.sonar.python.api.tree.PyNonlocalStatementTree; +import org.sonar.python.api.tree.PyPassStatementTree; +import org.sonar.python.api.tree.PyPrintStatementTree; +import org.sonar.python.api.tree.PyRaiseStatementTree; +import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PyTryStatementTree; +import org.sonar.python.api.tree.PyWhileStatementTree; +import org.sonar.python.api.tree.PyWithItemTree; +import org.sonar.python.api.tree.PyWithStatementTree; +import org.sonar.python.api.tree.PyYieldExpressionTree; +import org.sonar.python.api.tree.PyYieldStatementTree; +import org.sonar.python.api.tree.Tree; +import org.sonar.python.parser.RuleTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +public class PythonTreeMakerTest extends RuleTest { + + private final PythonTreeMaker treeMaker = new PythonTreeMaker(); + + @Test + public void fileInputTreeOnEmptyFile() { + PyFileInputTree pyTree = parse("", treeMaker::fileInput); + assertThat(pyTree.statements()).isEmpty(); + } + + @Test + public void unexpected_statement_should_throw_an_exception() { + try { + parse("", treeMaker::statement); + fail("unexpected ASTNode type for statement should not succeed to be translated to Strongly typed AST"); + } catch (IllegalStateException iae) { + assertThat(iae).hasMessage("Statement FILE_INPUT not correctly translated to strongly typed AST"); + } + } + + @Test + public void verify_expected_statement() { + Map> testData = new HashMap<>(); + testData.put("pass", PyPassStatementTree.class); + testData.put("print 'foo'", PyPrintStatementTree.class); + testData.put("exec foo", PyExecStatementTree.class); + testData.put("assert foo", PyAssertStatementTree.class); + testData.put("del foo", PyDelStatementTree.class); + testData.put("return foo", PyReturnStatementTree.class); + testData.put("yield foo", PyYieldStatementTree.class); + testData.put("raise foo", PyRaiseStatementTree.class); + testData.put("break", PyBreakStatementTree.class); + testData.put("continue", PyContinueStatementTree.class); + testData.put("def foo():pass", PyFunctionDefTree.class); + testData.put("import foo", PyImportStatementTree.class); + testData.put("from foo import f", PyImportStatementTree.class); + testData.put("class toto:pass", PyClassDefTree.class); + testData.put("for foo in bar:pass", PyForStatementTree.class); + testData.put("async for foo in bar: pass", PyForStatementTree.class); + testData.put("global foo", PyGlobalStatementTree.class); + testData.put("nonlocal foo", PyNonlocalStatementTree.class); + testData.put("while cond: pass", PyWhileStatementTree.class); + testData.put("'foo'", PyExpressionStatementTree.class); + testData.put("try: this\nexcept Exception: pass", PyTryStatementTree.class); + testData.put("with foo, bar as qix : pass", PyWithStatementTree.class); + testData.put("async with foo, bar as qix : pass", PyWithStatementTree.class); + + testData.forEach((c,clazz) -> { + PyFileInputTree pyTree = parse(c, treeMaker::fileInput); + assertThat(pyTree.statements()).hasSize(1); + assertThat(pyTree.statements().get(0)).as(c).isInstanceOf(clazz); + }); + } + + @Test + public void IfStatement() { + setRootRule(PythonGrammar.IF_STMT); + PyIfStatementTree pyIfStatementTree = parse("if x: pass", treeMaker::ifStatement); + assertThat(pyIfStatementTree.keyword().getValue()).isEqualTo("if"); + assertThat(pyIfStatementTree.condition()).isInstanceOf(PyExpressionTree.class); + assertThat(pyIfStatementTree.isElif()).isFalse(); + assertThat(pyIfStatementTree.elifBranches()).isEmpty(); + assertThat(pyIfStatementTree.elseBranch()).isNull(); + assertThat(pyIfStatementTree.body()).hasSize(1); + + + pyIfStatementTree = parse("if x: pass\nelse: pass", treeMaker::ifStatement); + assertThat(pyIfStatementTree.keyword().getValue()).isEqualTo("if"); + assertThat(pyIfStatementTree.condition()).isInstanceOf(PyExpressionTree.class); + assertThat(pyIfStatementTree.isElif()).isFalse(); + assertThat(pyIfStatementTree.elifBranches()).isEmpty(); + PyElseStatementTree elseBranch = pyIfStatementTree.elseBranch(); + assertThat(elseBranch).isNotNull(); + assertThat(elseBranch.elseKeyword().getValue()).isEqualTo("else"); + assertThat(elseBranch.body()).hasSize(1); + + + pyIfStatementTree = parse("if x: pass\nelif y: pass", treeMaker::ifStatement); + assertThat(pyIfStatementTree.keyword().getValue()).isEqualTo("if"); + assertThat(pyIfStatementTree.condition()).isInstanceOf(PyExpressionTree.class); + assertThat(pyIfStatementTree.isElif()).isFalse(); + assertThat(pyIfStatementTree.elseBranch()).isNull(); + assertThat(pyIfStatementTree.elifBranches()).hasSize(1); + PyIfStatementTree elif = pyIfStatementTree.elifBranches().get(0); + assertThat(elif.condition()).isInstanceOf(PyExpressionTree.class); + assertThat(elif.isElif()).isTrue(); + assertThat(elif.elseBranch()).isNull(); + assertThat(elif.elifBranches()).isEmpty(); + assertThat(elif.body()).hasSize(1); + + pyIfStatementTree = parse("if x:\n pass", treeMaker::ifStatement); + assertThat(pyIfStatementTree.keyword().getValue()).isEqualTo("if"); + assertThat(pyIfStatementTree.condition()).isInstanceOf(PyExpressionTree.class); + assertThat(pyIfStatementTree.isElif()).isFalse(); + assertThat(pyIfStatementTree.elseBranch()).isNull(); + assertThat(pyIfStatementTree.elifBranches()).isEmpty(); + assertThat(pyIfStatementTree.body()).hasSize(1); + } + + @Test + public void printStatement() { + setRootRule(PythonGrammar.PRINT_STMT); + AstNode astNode = p.parse("print 'foo'"); + PyPrintStatementTree printStmt = treeMaker.printStatement(astNode); + assertThat(printStmt).isNotNull(); + assertThat(printStmt.printKeyword().getValue()).isEqualTo("print"); + assertThat(printStmt.expressions()).hasSize(1); + + astNode = p.parse("print 'foo', 'bar'"); + printStmt = treeMaker.printStatement(astNode); + assertThat(printStmt).isNotNull(); + assertThat(printStmt.printKeyword().getValue()).isEqualTo("print"); + assertThat(printStmt.expressions()).hasSize(2); + + astNode = p.parse("print >> 'foo'"); + printStmt = treeMaker.printStatement(astNode); + assertThat(printStmt).isNotNull(); + assertThat(printStmt.printKeyword().getValue()).isEqualTo("print"); + assertThat(printStmt.expressions()).hasSize(1); + } + + @Test + public void execStatement() { + setRootRule(PythonGrammar.EXEC_STMT); + AstNode astNode = p.parse("exec 'foo'"); + PyExecStatementTree execStatement = treeMaker.execStatement(astNode); + assertThat(execStatement).isNotNull(); + assertThat(execStatement.execKeyword().getValue()).isEqualTo("exec"); + assertThat(execStatement.expression()).isNotNull(); + assertThat(execStatement.globalsExpression()).isNull(); + assertThat(execStatement.localsExpression()).isNull(); + + astNode = p.parse("exec 'foo' in globals"); + execStatement = treeMaker.execStatement(astNode); + assertThat(execStatement).isNotNull(); + assertThat(execStatement.execKeyword().getValue()).isEqualTo("exec"); + assertThat(execStatement.expression()).isNotNull(); + assertThat(execStatement.globalsExpression()).isNotNull(); + assertThat(execStatement.localsExpression()).isNull(); + + astNode = p.parse("exec 'foo' in globals, locals"); + execStatement = treeMaker.execStatement(astNode); + assertThat(execStatement).isNotNull(); + assertThat(execStatement.execKeyword().getValue()).isEqualTo("exec"); + assertThat(execStatement.expression()).isNotNull(); + assertThat(execStatement.globalsExpression()).isNotNull(); + assertThat(execStatement.localsExpression()).isNotNull(); + + // TODO: exec stmt should parse exec ('foo', globals, locals); see https://docs.python.org/2/reference/simple_stmts.html#exec + } + + @Test + public void assertStatement() { + setRootRule(PythonGrammar.ASSERT_STMT); + AstNode astNode = p.parse("assert x"); + PyAssertStatementTree assertStatement = treeMaker.assertStatement(astNode); + assertThat(assertStatement).isNotNull(); + assertThat(assertStatement.assertKeyword().getValue()).isEqualTo("assert"); + assertThat(assertStatement.expressions()).hasSize(1); + + astNode = p.parse("assert x, y"); + assertStatement = treeMaker.assertStatement(astNode); + assertThat(assertStatement).isNotNull(); + assertThat(assertStatement.assertKeyword().getValue()).isEqualTo("assert"); + assertThat(assertStatement.expressions()).hasSize(2); + } + + @Test + public void passStatement() { + setRootRule(PythonGrammar.PASS_STMT); + AstNode astNode = p.parse("pass"); + PyPassStatementTree passStatement = treeMaker.passStatement(astNode); + assertThat(passStatement).isNotNull(); + assertThat(passStatement.passKeyword().getValue()).isEqualTo("pass"); + } + + @Test + public void delStatement() { + setRootRule(PythonGrammar.DEL_STMT); + AstNode astNode = p.parse("del foo"); + PyDelStatementTree passStatement = treeMaker.delStatement(astNode); + assertThat(passStatement).isNotNull(); + assertThat(passStatement.delKeyword().getValue()).isEqualTo("del"); + assertThat(passStatement.expressions()).hasSize(1); + + astNode = p.parse("del foo, bar"); + passStatement = treeMaker.delStatement(astNode); + assertThat(passStatement).isNotNull(); + assertThat(passStatement.delKeyword().getValue()).isEqualTo("del"); + assertThat(passStatement.expressions()).hasSize(2); + + astNode = p.parse("del *foo"); + passStatement = treeMaker.delStatement(astNode); + assertThat(passStatement).isNotNull(); + assertThat(passStatement.delKeyword().getValue()).isEqualTo("del"); + assertThat(passStatement.expressions()).hasSize(1); + } + + @Test + public void returnStatement() { + setRootRule(PythonGrammar.RETURN_STMT); + AstNode astNode = p.parse("return foo"); + PyReturnStatementTree returnStatement = treeMaker.returnStatement(astNode); + assertThat(returnStatement).isNotNull(); + assertThat(returnStatement.returnKeyword().getValue()).isEqualTo("return"); + assertThat(returnStatement.expressions()).hasSize(1); + + astNode = p.parse("return foo, bar"); + returnStatement = treeMaker.returnStatement(astNode); + assertThat(returnStatement).isNotNull(); + assertThat(returnStatement.returnKeyword().getValue()).isEqualTo("return"); + assertThat(returnStatement.expressions()).hasSize(2); + + astNode = p.parse("return"); + returnStatement = treeMaker.returnStatement(astNode); + assertThat(returnStatement).isNotNull(); + assertThat(returnStatement.returnKeyword().getValue()).isEqualTo("return"); + assertThat(returnStatement.expressions()).hasSize(0); + } + + @Test + public void yieldStatement() { + setRootRule(PythonGrammar.YIELD_STMT); + AstNode astNode = p.parse("yield foo"); + PyYieldStatementTree yieldStatement = treeMaker.yieldStatement(astNode); + assertThat(yieldStatement).isNotNull(); + PyYieldExpressionTree yieldExpression = yieldStatement.yieldExpression(); + assertThat(yieldExpression).isInstanceOf(PyYieldExpressionTree.class); + assertThat(yieldExpression.expressions()).hasSize(1); + + astNode = p.parse("yield foo, bar"); + yieldStatement = treeMaker.yieldStatement(astNode); + assertThat(yieldStatement).isNotNull(); + yieldExpression = yieldStatement.yieldExpression(); + assertThat(yieldExpression).isInstanceOf(PyYieldExpressionTree.class); + assertThat(yieldExpression.yieldKeyword().getValue()).isEqualTo("yield"); + assertThat(yieldExpression.fromKeyword()).isNull(); + assertThat(yieldExpression.expressions()).hasSize(2); + + astNode = p.parse("yield from foo"); + yieldStatement = treeMaker.yieldStatement(astNode); + assertThat(yieldStatement).isNotNull(); + yieldExpression = yieldStatement.yieldExpression(); + assertThat(yieldExpression).isInstanceOf(PyYieldExpressionTree.class); + assertThat(yieldExpression.yieldKeyword().getValue()).isEqualTo("yield"); + assertThat(yieldExpression.fromKeyword().getValue()).isEqualTo("from"); + assertThat(yieldExpression.expressions()).hasSize(1); + + astNode = p.parse("yield"); + yieldStatement = treeMaker.yieldStatement(astNode); + assertThat(yieldStatement).isNotNull(); + } + + @Test + public void raiseStatement() { + setRootRule(PythonGrammar.RAISE_STMT); + AstNode astNode = p.parse("raise foo"); + PyRaiseStatementTree raiseStatement = treeMaker.raiseStatement(astNode); + assertThat(raiseStatement).isNotNull(); + assertThat(raiseStatement.raiseKeyword().getValue()).isEqualTo("raise"); + assertThat(raiseStatement.fromKeyword()).isNull(); + assertThat(raiseStatement.fromExpression()).isNull(); + assertThat(raiseStatement.expressions()).hasSize(1); + + astNode = p.parse("raise foo, bar"); + raiseStatement = treeMaker.raiseStatement(astNode); + assertThat(raiseStatement).isNotNull(); + assertThat(raiseStatement.raiseKeyword().getValue()).isEqualTo("raise"); + assertThat(raiseStatement.fromKeyword()).isNull(); + assertThat(raiseStatement.fromExpression()).isNull(); + assertThat(raiseStatement.expressions()).hasSize(2); + + astNode = p.parse("raise foo from bar"); + raiseStatement = treeMaker.raiseStatement(astNode); + assertThat(raiseStatement).isNotNull(); + assertThat(raiseStatement.raiseKeyword().getValue()).isEqualTo("raise"); + assertThat(raiseStatement.fromKeyword().getValue()).isEqualTo("from"); + assertThat(raiseStatement.fromExpression()).isNotNull(); + assertThat(raiseStatement.expressions()).hasSize(1); + + astNode = p.parse("raise"); + raiseStatement = treeMaker.raiseStatement(astNode); + assertThat(raiseStatement).isNotNull(); + assertThat(raiseStatement.raiseKeyword().getValue()).isEqualTo("raise"); + assertThat(raiseStatement.fromKeyword()).isNull(); + assertThat(raiseStatement.fromExpression()).isNull(); + assertThat(raiseStatement.expressions()).isEmpty(); + } + + @Test + public void breakStatement() { + setRootRule(PythonGrammar.BREAK_STMT); + AstNode astNode = p.parse("break"); + PyBreakStatementTree breakStatement = treeMaker.breakStatement(astNode); + assertThat(breakStatement).isNotNull(); + assertThat(breakStatement.breakKeyword().getValue()).isEqualTo("break"); + } + + @Test + public void continueStatement() { + setRootRule(PythonGrammar.CONTINUE_STMT); + AstNode astNode = p.parse("continue"); + PyContinueStatementTree continueStatement = treeMaker.continueStatement(astNode); + assertThat(continueStatement).isNotNull(); + assertThat(continueStatement.continueKeyword().getValue()).isEqualTo("continue"); + } + + @Test + public void importStatement() { + setRootRule(PythonGrammar.IMPORT_STMT); + AstNode astNode = p.parse("import foo"); + PyImportNameTree importStatement = (PyImportNameTree) treeMaker.importStatement(astNode); + assertThat(importStatement).isNotNull(); + assertThat(importStatement.importKeyword().getValue()).isEqualTo("import"); + assertThat(importStatement.modules()).hasSize(1); + PyAliasedNameTree importedName1 = importStatement.modules().get(0); + assertThat(importedName1.dottedName().names()).hasSize(1); + assertThat(importedName1.dottedName().names().get(0).name()).isEqualTo("foo"); + + astNode = p.parse("import foo as f"); + importStatement = (PyImportNameTree) treeMaker.importStatement(astNode); + assertThat(importStatement).isNotNull(); + assertThat(importStatement.importKeyword().getValue()).isEqualTo("import"); + assertThat(importStatement.modules()).hasSize(1); + importedName1 = importStatement.modules().get(0); + assertThat(importedName1.dottedName().names()).hasSize(1); + assertThat(importedName1.dottedName().names().get(0).name()).isEqualTo("foo"); + assertThat(importedName1.asKeyword().getValue()).isEqualTo("as"); + assertThat(importedName1.alias().name()).isEqualTo("f"); + + astNode = p.parse("import foo.bar"); + importStatement = (PyImportNameTree) treeMaker.importStatement(astNode); + assertThat(importStatement).isNotNull(); + assertThat(importStatement.importKeyword().getValue()).isEqualTo("import"); + assertThat(importStatement.modules()).hasSize(1); + importedName1 = importStatement.modules().get(0); + assertThat(importedName1.dottedName().names()).hasSize(2); + assertThat(importedName1.dottedName().names().get(0).name()).isEqualTo("foo"); + assertThat(importedName1.dottedName().names().get(1).name()).isEqualTo("bar"); + + astNode = p.parse("import foo, bar"); + importStatement = (PyImportNameTree) treeMaker.importStatement(astNode); + assertThat(importStatement).isNotNull(); + assertThat(importStatement.importKeyword().getValue()).isEqualTo("import"); + assertThat(importStatement.modules()).hasSize(2); + importedName1 = importStatement.modules().get(0); + assertThat(importedName1.dottedName().names()).hasSize(1); + assertThat(importedName1.dottedName().names().get(0).name()).isEqualTo("foo"); + PyAliasedNameTree importedName2 = importStatement.modules().get(1); + assertThat(importedName2.dottedName().names()).hasSize(1); + assertThat(importedName2.dottedName().names().get(0).name()).isEqualTo("bar"); + } + + @Test + public void importFromStatement() { + setRootRule(PythonGrammar.IMPORT_STMT); + AstNode astNode = p.parse("from foo import f"); + PyImportFromTree importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement).isNotNull(); + assertThat(importStatement.importKeyword().getValue()).isEqualTo("import"); + assertThat(importStatement.dottedPrefixForModule()).isEmpty(); + assertThat(importStatement.fromKeyword().getValue()).isEqualTo("from"); + assertThat(importStatement.module().names().get(0).name()).isEqualTo("foo"); + assertThat(importStatement.isWildcardImport()).isFalse(); + assertThat(importStatement.wildcard()).isNull(); + assertThat(importStatement.importedNames()).hasSize(1); + PyAliasedNameTree aliasedNameTree = importStatement.importedNames().get(0); + assertThat(aliasedNameTree.asKeyword()).isNull(); + assertThat(aliasedNameTree.alias()).isNull(); + assertThat(aliasedNameTree.dottedName().names().get(0).name()).isEqualTo("f"); + + astNode = p.parse("from .foo import f"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.dottedPrefixForModule()).hasSize(1); + assertThat(importStatement.dottedPrefixForModule().get(0).getValue()).isEqualTo("."); + assertThat(importStatement.module().names().get(0).name()).isEqualTo("foo"); + + astNode = p.parse("from ..foo import f"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.dottedPrefixForModule()).hasSize(2); + assertThat(importStatement.dottedPrefixForModule().get(0).getValue()).isEqualTo("."); + assertThat(importStatement.dottedPrefixForModule().get(1).getValue()).isEqualTo("."); + assertThat(importStatement.module().names().get(0).name()).isEqualTo("foo"); + + astNode = p.parse("from . import f"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.dottedPrefixForModule()).hasSize(1); + assertThat(importStatement.dottedPrefixForModule().get(0).getValue()).isEqualTo("."); + assertThat(importStatement.module()).isNull(); + + astNode = p.parse("from foo import f as g"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.importedNames()).hasSize(1); + aliasedNameTree = importStatement.importedNames().get(0); + assertThat(aliasedNameTree.asKeyword().getValue()).isEqualTo("as"); + assertThat(aliasedNameTree.alias().name()).isEqualTo("g"); + assertThat(aliasedNameTree.dottedName().names().get(0).name()).isEqualTo("f"); + + astNode = p.parse("from foo import f as g, h"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.importedNames()).hasSize(2); + PyAliasedNameTree aliasedNameTree1 = importStatement.importedNames().get(0); + assertThat(aliasedNameTree1.asKeyword().getValue()).isEqualTo("as"); + assertThat(aliasedNameTree1.alias().name()).isEqualTo("g"); + assertThat(aliasedNameTree1.dottedName().names().get(0).name()).isEqualTo("f"); + + PyAliasedNameTree aliasedNameTree2 = importStatement.importedNames().get(1); + assertThat(aliasedNameTree2.asKeyword()).isNull(); + assertThat(aliasedNameTree2.alias()).isNull(); + assertThat(aliasedNameTree2.dottedName().names().get(0).name()).isEqualTo("h"); + + astNode = p.parse("from foo import *"); + importStatement = (PyImportFromTree) treeMaker.importStatement(astNode); + assertThat(importStatement.importedNames()).isNull(); + assertThat(importStatement.isWildcardImport()).isTrue(); + assertThat(importStatement.wildcard().getValue()).isEqualTo("*"); + } + + @Test + public void globalStatement() { + setRootRule(PythonGrammar.GLOBAL_STMT); + AstNode astNode = p.parse("global foo"); + PyGlobalStatementTree globalStatement = treeMaker.globalStatement(astNode); + assertThat(globalStatement.globalKeyword().getValue()).isEqualTo("global"); + assertThat(globalStatement.variables()).hasSize(1); + assertThat(globalStatement.variables().get(0).name()).isEqualTo("foo"); + + astNode = p.parse("global foo, bar"); + globalStatement = treeMaker.globalStatement(astNode); + assertThat(globalStatement.globalKeyword().getValue()).isEqualTo("global"); + assertThat(globalStatement.variables()).hasSize(2); + assertThat(globalStatement.variables().get(0).name()).isEqualTo("foo"); + assertThat(globalStatement.variables().get(1).name()).isEqualTo("bar"); + } + + @Test + public void nonlocalStatement() { + setRootRule(PythonGrammar.NONLOCAL_STMT); + AstNode astNode = p.parse("nonlocal foo"); + PyNonlocalStatementTree nonlocalStatement = treeMaker.nonlocalStatement(astNode); + assertThat(nonlocalStatement.nonlocalKeyword().getValue()).isEqualTo("nonlocal"); + assertThat(nonlocalStatement.variables()).hasSize(1); + assertThat(nonlocalStatement.variables().get(0).name()).isEqualTo("foo"); + + astNode = p.parse("nonlocal foo, bar"); + nonlocalStatement = treeMaker.nonlocalStatement(astNode); + assertThat(nonlocalStatement.nonlocalKeyword().getValue()).isEqualTo("nonlocal"); + assertThat(nonlocalStatement.variables()).hasSize(2); + assertThat(nonlocalStatement.variables().get(0).name()).isEqualTo("foo"); + assertThat(nonlocalStatement.variables().get(1).name()).isEqualTo("bar"); + } + + @Test + public void funcdef_statement() { + setRootRule(PythonGrammar.FUNCDEF); + AstNode astNode = p.parse("def func(): pass"); + PyFunctionDefTree functionDefTree = treeMaker.funcDefStatement(astNode); + assertThat(functionDefTree.name()).isNotNull(); + assertThat(functionDefTree.name().name()).isEqualTo("func"); + assertThat(functionDefTree.body()).hasSize(1); + assertThat(functionDefTree.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + // TODO + assertThat(functionDefTree.typedArgs()).isNull(); + assertThat(functionDefTree.decorators()).isNull(); + assertThat(functionDefTree.asyncKeyword()).isNull(); + assertThat(functionDefTree.colon()).isNull(); + assertThat(functionDefTree.defKeyword()).isNull(); + assertThat(functionDefTree.dash()).isNull(); + assertThat(functionDefTree.gt()).isNull(); + assertThat(functionDefTree.leftPar()).isNull(); + assertThat(functionDefTree.rightPar()).isNull(); + + } + + @Test + public void classdef_statement() { + setRootRule(PythonGrammar.CLASSDEF); + AstNode astNode = p.parse("class clazz: pass"); + PyClassDefTree classDefTree = treeMaker.classDefStatement(astNode); + assertThat(classDefTree.name()).isNotNull(); + assertThat(classDefTree.name().name()).isEqualTo("clazz"); + assertThat(classDefTree.body()).hasSize(1); + assertThat(classDefTree.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(classDefTree.args()).isNull(); + assertThat(classDefTree.decorators()).isNull(); + } + + @Test + public void for_statement() { + setRootRule(PythonGrammar.FOR_STMT); + AstNode astNode = p.parse("for foo in bar: pass"); + PyForStatementTree pyForStatementTree = treeMaker.forStatement(astNode); + assertThat(pyForStatementTree.expressions()).hasSize(1); + assertThat(pyForStatementTree.testExpressions()).hasSize(1); + assertThat(pyForStatementTree.body()).hasSize(1); + assertThat(pyForStatementTree.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(pyForStatementTree.elseBody()).isEmpty(); + assertThat(pyForStatementTree.isAsync()).isFalse(); + assertThat(pyForStatementTree.asyncKeyword()).isNull(); + + astNode = p.parse("for foo in bar:\n pass\nelse:\n pass"); + pyForStatementTree = treeMaker.forStatement(astNode); + assertThat(pyForStatementTree.expressions()).hasSize(1); + assertThat(pyForStatementTree.testExpressions()).hasSize(1); + assertThat(pyForStatementTree.body()).hasSize(1); + assertThat(pyForStatementTree.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(pyForStatementTree.elseBody()).hasSize(1); + assertThat(pyForStatementTree.elseBody().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + + // TODO + assertThat(pyForStatementTree.forKeyword()).isNull(); + assertThat(pyForStatementTree.inKeyword()).isNull(); + assertThat(pyForStatementTree.colon()).isNull(); + assertThat(pyForStatementTree.elseKeyword()).isNull(); + assertThat(pyForStatementTree.elseColon()).isNull(); + } + + @Test + public void while_statement() { + setRootRule(PythonGrammar.WHILE_STMT); + AstNode astNode = p.parse("while foo : pass"); + PyWhileStatementTreeImpl whileStatement = treeMaker.whileStatement(astNode); + assertThat(whileStatement.condition()).isNotNull(); + assertThat(whileStatement.body()).hasSize(1); + assertThat(whileStatement.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(whileStatement.elseBody()).isEmpty(); + + astNode = p.parse("while foo:\n pass\nelse:\n pass"); + whileStatement = treeMaker.whileStatement(astNode); + assertThat(whileStatement.condition()).isNotNull(); + assertThat(whileStatement.body()).hasSize(1); + assertThat(whileStatement.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(whileStatement.elseBody()).hasSize(1); + assertThat(whileStatement.elseBody().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + + // TODO + assertThat(whileStatement.whileKeyword()).isNull(); + assertThat(whileStatement.colon()).isNull(); + assertThat(whileStatement.elseKeyword()).isNull(); + assertThat(whileStatement.elseColon()).isNull(); + + } + + @Test + public void expression_statement() { + setRootRule(PythonGrammar.EXPRESSION_STMT); + AstNode astNode = p.parse("'foo'"); + PyExpressionStatementTree expressionStatement = treeMaker.expressionStatement(astNode); + assertThat(expressionStatement.expressions()).hasSize(1); + + astNode = p.parse("'foo', 'bar'"); + expressionStatement = treeMaker.expressionStatement(astNode); + assertThat(expressionStatement.expressions()).hasSize(2); + } + + @Test + public void try_statement() { + setRootRule(PythonGrammar.TRY_STMT); + AstNode astNode = p.parse("try: pass\nexcept Error: pass"); + PyTryStatementTree tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.body()).hasSize(1); + assertThat(tryStatement.elseClause()).isNull(); + assertThat(tryStatement.finallyClause()).isNull(); + assertThat(tryStatement.exceptClauses()).hasSize(1); + assertThat(tryStatement.exceptClauses().get(0).exceptKeyword().getValue()).isEqualTo("except"); + assertThat(tryStatement.exceptClauses().get(0).body()).hasSize(1); + + astNode = p.parse("try: pass\nexcept Error: pass\nexcept Error: pass"); + tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.elseClause()).isNull(); + assertThat(tryStatement.finallyClause()).isNull(); + assertThat(tryStatement.exceptClauses()).hasSize(2); + + astNode = p.parse("try: pass\nexcept Error: pass\nfinally: pass"); + tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.elseClause()).isNull(); + assertThat(tryStatement.exceptClauses()).hasSize(1); + assertThat(tryStatement.finallyClause()).isNotNull(); + assertThat(tryStatement.finallyClause().finallyKeyword().getValue()).isEqualTo("finally"); + assertThat(tryStatement.finallyClause().body()).hasSize(1); + + astNode = p.parse("try: pass\nexcept Error: pass\nelse: pass"); + tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.exceptClauses()).hasSize(1); + assertThat(tryStatement.finallyClause()).isNull(); + assertThat(tryStatement.elseClause().elseKeyword().getValue()).isEqualTo("else"); + assertThat(tryStatement.elseClause().body()).hasSize(1); + + astNode = p.parse("try: pass\nexcept Error as e: pass"); + tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.exceptClauses()).hasSize(1); + PyExceptClauseTree exceptClause = tryStatement.exceptClauses().get(0); + assertThat(exceptClause.asKeyword().getValue()).isEqualTo("as"); + assertThat(exceptClause.commaToken()).isNull(); + assertThat(exceptClause.exceptionInstance()).isNotNull(); + + astNode = p.parse("try: pass\nexcept Error, e: pass"); + tryStatement = treeMaker.tryStatement(astNode); + assertThat(tryStatement.tryKeyword().getValue()).isEqualTo("try"); + assertThat(tryStatement.exceptClauses()).hasSize(1); + exceptClause = tryStatement.exceptClauses().get(0); + assertThat(exceptClause.asKeyword()).isNull(); + assertThat(exceptClause.commaToken().getValue()).isEqualTo(","); + assertThat(exceptClause.exceptionInstance()).isNotNull(); + } + + @Test + public void async_statement() { + setRootRule(PythonGrammar.ASYNC_STMT); + AstNode astNode = p.parse("async for foo in bar: pass"); + PyForStatementTree pyForStatementTree = new PythonTreeMaker().forStatement(astNode); + assertThat(pyForStatementTree.isAsync()).isTrue(); + assertThat(pyForStatementTree.asyncKeyword().getValue()).isEqualTo("async"); + assertThat(pyForStatementTree.expressions()).hasSize(1); + assertThat(pyForStatementTree.testExpressions()).hasSize(1); + assertThat(pyForStatementTree.body()).hasSize(1); + assertThat(pyForStatementTree.body().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + assertThat(pyForStatementTree.elseBody()).isEmpty(); + + PyWithStatementTree withStatement = parse("async with foo : pass", treeMaker::withStatement); + assertThat(withStatement.isAsync()).isTrue(); + assertThat(withStatement.asyncKeyword().getValue()).isEqualTo("async"); + PyWithItemTree pyWithItemTree = withStatement.withItems().get(0); + assertThat(pyWithItemTree.test()).isNotNull(); + assertThat(pyWithItemTree.as()).isNull(); + assertThat(pyWithItemTree.expression()).isNull(); + assertThat(withStatement.statements()).hasSize(1); + assertThat(withStatement.statements().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + } + + @Test + public void with_statement() { + setRootRule(PythonGrammar.WITH_STMT); + PyWithStatementTree withStatement = parse("with foo : pass", treeMaker::withStatement); + assertThat(withStatement.isAsync()).isFalse(); + assertThat(withStatement.asyncKeyword()).isNull(); + assertThat(withStatement.withItems()).hasSize(1); + PyWithItemTree pyWithItemTree = withStatement.withItems().get(0); + assertThat(pyWithItemTree.test()).isNotNull(); + assertThat(pyWithItemTree.as()).isNull(); + assertThat(pyWithItemTree.expression()).isNull(); + assertThat(withStatement.statements()).hasSize(1); + assertThat(withStatement.statements().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + + withStatement = parse("with foo as bar, qix : pass", treeMaker::withStatement); + assertThat(withStatement.withItems()).hasSize(2); + pyWithItemTree = withStatement.withItems().get(0); + assertThat(pyWithItemTree.test()).isNotNull(); + assertThat(pyWithItemTree.as()).isNotNull(); + assertThat(pyWithItemTree.expression()).isNotNull(); + pyWithItemTree = withStatement.withItems().get(1); + assertThat(pyWithItemTree.test()).isNotNull(); + assertThat(pyWithItemTree.as()).isNull(); + assertThat(pyWithItemTree.expression()).isNull(); + assertThat(withStatement.statements()).hasSize(1); + assertThat(withStatement.statements().get(0).is(Tree.Kind.PASS_STMT)).isTrue(); + } + + private T parse(String code, Function func) { + return func.apply(p.parse(code)); + } +} diff --git a/sonar-python-plugin/pom.xml b/sonar-python-plugin/pom.xml index 05d2b2689c..5c94a0a1cb 100644 --- a/sonar-python-plugin/pom.xml +++ b/sonar-python-plugin/pom.xml @@ -135,7 +135,7 @@ - 2800000 + 2900000 2600000 ${project.build.directory}/${project.build.finalName}.jar diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonHighlighter.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonHighlighter.java index 4d7eab3556..cb16445125 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonHighlighter.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonHighlighter.java @@ -31,7 +31,7 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.highlighting.NewHighlighting; import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.python.PythonCheck; +import org.sonar.python.PythonCheckAstNode; import org.sonar.python.PythonVisitor; import org.sonar.python.TokenLocation; import org.sonar.python.api.PythonGrammar; @@ -98,7 +98,7 @@ public PythonHighlighter(SensorContext context, InputFile inputFile) { @Override public Set subscribedKinds() { - return PythonCheck.immutableSet( + return PythonCheckAstNode.immutableSet( PythonGrammar.FUNCDEF, PythonGrammar.CLASSDEF, PythonGrammar.FILE_INPUT); diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonScanner.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonScanner.java index 15cafbec16..95288d7ee1 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonScanner.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonScanner.java @@ -19,6 +19,7 @@ */ package org.sonar.plugins.python; +import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.Grammar; import com.sonar.sslr.api.RecognitionException; import com.sonar.sslr.impl.Parser; @@ -45,9 +46,11 @@ import org.sonar.python.PythonConfiguration; import org.sonar.python.PythonFile; import org.sonar.python.PythonVisitorContext; +import org.sonar.python.api.tree.PyFileInputTree; import org.sonar.python.metrics.FileLinesVisitor; import org.sonar.python.metrics.FileMetrics; import org.sonar.python.parser.PythonParser; +import org.sonar.python.tree.PythonTreeMaker; public class PythonScanner { @@ -89,7 +92,9 @@ private void scanFile(InputFile inputFile) { PythonFile pythonFile = SonarQubePythonFile.create(inputFile); PythonVisitorContext visitorContext; try { - visitorContext = new PythonVisitorContext(parser.parse(pythonFile.content()), pythonFile); + AstNode astNode = parser.parse(pythonFile.content()); + PyFileInputTree parse = new PythonTreeMaker().fileInput(astNode); + visitorContext = new PythonVisitorContext(astNode, parse, pythonFile); saveMeasures(inputFile, visitorContext); } catch (RecognitionException e) { visitorContext = new PythonVisitorContext(pythonFile, e); diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/cpd/PythonCpdAnalyzer.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/cpd/PythonCpdAnalyzer.java index 9741b84bb5..576bcc0dae 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/cpd/PythonCpdAnalyzer.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/cpd/PythonCpdAnalyzer.java @@ -40,7 +40,7 @@ public PythonCpdAnalyzer(SensorContext context) { } public void pushCpdTokens(InputFile inputFile, PythonVisitorContext visitorContext) { - AstNode root = visitorContext.rootTree(); + AstNode root = visitorContext.rootAstNode(); if (root != null) { NewCpdTokens cpdTokens = context.newCpdTokens().onFile(inputFile); List tokens = root.getTokens();