All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.python.semantic.v2.ReadUsagesVisitor Maven / Gradle / Ivy

The newest version!
/*
 * SonarQube Python Plugin
 * Copyright (C) 2011-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * 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 Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonar.python.semantic.v2;

import java.util.Map;
import org.sonar.plugins.python.api.tree.ClassDef;
import org.sonar.plugins.python.api.tree.ComprehensionExpression;
import org.sonar.plugins.python.api.tree.ComprehensionFor;
import org.sonar.plugins.python.api.tree.DictCompExpression;
import org.sonar.plugins.python.api.tree.FileInput;
import org.sonar.plugins.python.api.tree.FunctionDef;
import org.sonar.plugins.python.api.tree.LambdaExpression;
import org.sonar.plugins.python.api.tree.Name;
import org.sonar.plugins.python.api.tree.Parameter;
import org.sonar.plugins.python.api.tree.Tree;
import org.sonar.plugins.python.api.tree.TypeAnnotation;

/**
 * Read (i.e. non-binding) usages have to be visited in a second phase.
 * They can't be visited in the same phase as write (i.e. binding) usages,
 * since a read usage may appear in the syntax tree "before" it's declared (written).
 */
public class ReadUsagesVisitor extends ScopeVisitor {
  public ReadUsagesVisitor(Map scopesByRootTree) {
    super(scopesByRootTree);
  }

  @Override
  public void visitFileInput(FileInput tree) {
    enterScope(tree);
    super.visitFileInput(tree);
  }

  @Override
  public void visitFunctionDef(FunctionDef pyFunctionDefTree) {
    scan(pyFunctionDefTree.decorators());
    enterScope(pyFunctionDefTree);
    scan(pyFunctionDefTree.name());
    scan(pyFunctionDefTree.typeParams());
    scan(pyFunctionDefTree.parameters());
    scan(pyFunctionDefTree.returnTypeAnnotation());
    scan(pyFunctionDefTree.body());
    leaveScope();
  }

  @Override
  public void visitParameter(Parameter parameter) {
    // parameter default value should not be in the function scope.
    Tree currentScopeTree = leaveScope();
    scan(parameter.defaultValue());
    enterScope(currentScopeTree);
    scan(parameter.name());
    scan(parameter.typeAnnotation());
  }

  @Override
  public void visitLambda(LambdaExpression pyLambdaExpressionTree) {
    enterScope(pyLambdaExpressionTree);
    super.visitLambda(pyLambdaExpressionTree);
    leaveScope();
  }

  @Override
  public void visitPyListOrSetCompExpression(ComprehensionExpression tree) {
    enterScope(tree);
    scan(tree.resultExpression());
    ComprehensionFor comprehensionFor = tree.comprehensionFor();
    scan(comprehensionFor.loopExpression());
    leaveScope();
    scan(comprehensionFor.iterable());
    enterScope(tree);
    scan(comprehensionFor.nestedClause());
    leaveScope();
  }

  @Override
  public void visitDictCompExpression(DictCompExpression tree) {
    enterScope(tree);
    scan(tree.keyExpression());
    scan(tree.valueExpression());
    ComprehensionFor comprehensionFor = tree.comprehensionFor();
    scan(comprehensionFor.loopExpression());
    leaveScope();
    scan(comprehensionFor.iterable());
    enterScope(tree);
    scan(comprehensionFor.nestedClause());
    leaveScope();
  }

  @Override
  public void visitTypeAnnotation(TypeAnnotation tree) {
    if (tree.is(Tree.Kind.PARAMETER_TYPE_ANNOTATION) || tree.is(Tree.Kind.RETURN_TYPE_ANNOTATION)) {
      // The scope of the type annotations on a function declaration should be the scope enclosing the function, and not the scope of
      // the function body itself. Note that this code assumes that we already entered the function body scope by visiting the type
      // annotations, so there should always be a scope to pop out and return to here.
      Tree currentScopeTree = leaveScope();
      super.visitTypeAnnotation(tree);
      enterScope(currentScopeTree);
      super.visitTypeAnnotation(tree);
    } else {
      super.visitTypeAnnotation(tree);
    }
  }

  @Override
  public void visitClassDef(ClassDef classDef) {
    scan(classDef.args());
    scan(classDef.decorators());
    enterScope(classDef);
    scan(classDef.name());
    scan(classDef.body());
    leaveScope();
  }

  @Override
  public void visitName(Name name) {
    if (!name.isVariable()) {
      return;
    }
    addSymbolUsage(name);
    super.visitName(name);
  }

  private void addSymbolUsage(Name name) {
    var scope = currentScope();
    var symbol = scope.resolve(name.name());
    if (symbol != null && symbol.usages().stream().noneMatch(usage -> usage.tree().equals(name))) {
      symbol.addUsage(name, UsageV2.Kind.OTHER);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy