
org.sonar.php.checks.utils.ReadWriteUsages Maven / Gradle / Ivy
The newest version!
/*
* SonarQube PHP Plugin
* Copyright (C) 2010-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 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.php.checks.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.sonar.php.tree.symbols.Scope;
import org.sonar.plugins.php.api.symbols.Symbol;
import org.sonar.plugins.php.api.symbols.SymbolTable;
import org.sonar.plugins.php.api.tree.Tree;
import org.sonar.plugins.php.api.tree.Tree.Kind;
import org.sonar.plugins.php.api.tree.declaration.VariableDeclarationTree;
import org.sonar.plugins.php.api.tree.expression.ArrayAccessTree;
import org.sonar.plugins.php.api.tree.expression.ArrayAssignmentPatternElementTree;
import org.sonar.plugins.php.api.tree.expression.AssignmentExpressionTree;
import org.sonar.plugins.php.api.tree.expression.ExpressionTree;
import org.sonar.plugins.php.api.tree.expression.FunctionExpressionTree;
import org.sonar.plugins.php.api.tree.expression.LexicalVariablesTree;
import org.sonar.plugins.php.api.tree.expression.VariableIdentifierTree;
import org.sonar.plugins.php.api.tree.expression.VariableTree;
import org.sonar.plugins.php.api.tree.lexical.SyntaxToken;
import org.sonar.plugins.php.api.tree.statement.ForEachStatementTree;
import org.sonar.plugins.php.api.visitors.PHPVisitorCheck;
public class ReadWriteUsages {
private final SymbolTable symbolTable;
private final Set writes = new HashSet<>();
private final Set readAssignment = new HashSet<>();
private final Set declarations = new HashSet<>();
private final Map> inheritedVariablesByParent = new HashMap<>();
private final Map parentSymbolByInheritedReference = new HashMap<>();
public ReadWriteUsages(Tree tree, SymbolTable symbolTable) {
this.symbolTable = symbolTable;
tree.accept(new UsageVisitor());
}
public boolean isRead(Symbol symbol) {
return hasReadUsage(symbol)
|| inheritedVariablesByParent.getOrDefault(symbol, Collections.emptyList()).stream().anyMatch(this::isRead)
|| hasParentWhichIsRead(symbol);
}
public List getWritesSorted(Symbol symbol) {
var allReferences = new ArrayList<>(symbol.usages());
allReferences.add(symbol.declaration().token());
return allReferences.stream()
.filter(writes::contains)
.sorted(Comparator.comparing(SyntaxToken::line).reversed())
.toList();
}
private boolean hasReadUsage(Symbol symbol) {
List allReferences = new ArrayList<>();
allReferences.add(symbol.declaration().token());
allReferences.addAll(symbol.usages());
return allReferences.stream()
.anyMatch(t -> (!writes.contains(t) && !declarations.contains(t)) || readAssignment.contains(t));
}
private boolean hasParentWhichIsRead(Symbol symbol) {
Symbol parent = parentSymbolByInheritedReference.get(symbol);
return parent != null && hasReadUsage(parent);
}
private class UsageVisitor extends PHPVisitorCheck {
@Override
public void visitVariableDeclaration(VariableDeclarationTree tree) {
visitAssignedVariable(tree.identifier());
super.visitVariableDeclaration(tree);
}
@Override
public void visitAssignmentExpression(AssignmentExpressionTree tree) {
if (!tree.getParent().is(Kind.EXPRESSION_STATEMENT) && !tree.operator().startsWith("=")) {
// compound assignment used as operand
visitReadAssignedVariable(tree.variable());
} else {
visitAssignedVariable(tree.variable());
}
super.visitAssignmentExpression(tree);
}
@Override
public void visitArrayAssignmentPatternElement(ArrayAssignmentPatternElementTree tree) {
visitAssignedVariable(tree.variable());
super.visitArrayAssignmentPatternElement(tree);
}
@Override
public void visitForEachStatement(ForEachStatementTree tree) {
ExpressionTree key = tree.key();
if (key != null) {
visitAssignedVariable(key);
}
visitAssignedVariable(tree.value());
super.visitForEachStatement(tree);
}
private void visitAssignedVariable(Tree tree) {
if (tree.is(Tree.Kind.ARRAY_ACCESS)) {
visitReadAssignedVariable(((ArrayAccessTree) tree).object());
return;
} else if (!tree.is(Tree.Kind.VARIABLE_IDENTIFIER)) {
return;
}
writes.add(((VariableIdentifierTree) tree).token());
}
private void visitReadAssignedVariable(Tree tree) {
if (tree.is(Tree.Kind.ARRAY_ACCESS)) {
visitReadAssignedVariable(((ArrayAccessTree) tree).object());
return;
} else if (!tree.is(Tree.Kind.VARIABLE_IDENTIFIER)) {
return;
}
SyntaxToken token = ((VariableIdentifierTree) tree).token();
writes.add(token);
readAssignment.add(token);
}
@Override
public void visitFunctionExpression(FunctionExpressionTree tree) {
LexicalVariablesTree lexicalVars = tree.lexicalVars();
if (lexicalVars != null) {
Scope scope = symbolTable.getScopeFor(tree);
for (VariableTree variableTree : lexicalVars.variables()) {
visitLexicalVar(scope, variableTree);
}
}
super.visitFunctionExpression(tree);
}
private void visitLexicalVar(Scope scope, VariableTree variableTree) {
Scope parentScope = scope.outer();
VariableIdentifierTree variableIdentifier = null;
if (variableTree.is(Tree.Kind.VARIABLE_IDENTIFIER)) {
variableIdentifier = (VariableIdentifierTree) variableTree;
Symbol parentScopeSymbol = parentScope.getSymbol(variableIdentifier.text());
Symbol symbol = scope.getSymbol(variableIdentifier.text());
if (parentScopeSymbol != null && symbol != null) {
inheritedVariablesByParent.computeIfAbsent(parentScopeSymbol, key -> new ArrayList<>()).add(symbol);
}
} else if (variableTree.is(Tree.Kind.REFERENCE_VARIABLE) && variableTree.variableExpression().is(Tree.Kind.VARIABLE_IDENTIFIER)) {
variableIdentifier = (VariableIdentifierTree) variableTree.variableExpression();
Symbol parentScopeSymbol = parentScope.getSymbol(variableIdentifier.text());
Symbol symbol = scope.getSymbol(variableIdentifier.text());
if (parentScopeSymbol != null && symbol != null) {
inheritedVariablesByParent.computeIfAbsent(parentScopeSymbol, key -> new ArrayList<>()).add(symbol);
parentSymbolByInheritedReference.put(symbol, parentScopeSymbol);
}
}
if (variableIdentifier != null) {
declarations.add(variableIdentifier.token());
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy