org.sonar.python.semantic.Scope Maven / Gradle / Ivy
/*
* SonarQube Python Plugin
* Copyright (C) 2011-2020 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.semantic;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.plugins.python.api.PythonFile;
import org.sonar.plugins.python.api.symbols.AmbiguousSymbol;
import org.sonar.plugins.python.api.symbols.FunctionSymbol;
import org.sonar.plugins.python.api.symbols.Symbol;
import org.sonar.plugins.python.api.symbols.Usage;
import org.sonar.plugins.python.api.tree.ClassDef;
import org.sonar.plugins.python.api.tree.FunctionDef;
import org.sonar.plugins.python.api.tree.ImportFrom;
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.python.tree.FunctionDefImpl;
import org.sonar.python.types.InferredTypes;
import org.sonar.python.types.TypeShed;
import static org.sonar.python.semantic.SymbolUtils.isTypeShedFile;
class Scope {
final Tree rootTree;
private PythonFile pythonFile;
private String fullyQualifiedModuleName;
private final Scope parent;
final Map symbolsByName = new HashMap<>();
private final Set symbols = new HashSet<>();
final Set builtinSymbols = new HashSet<>();
private final Set globalNames = new HashSet<>();
private final Set nonlocalNames = new HashSet<>();
final Map instanceAttributesByName = new HashMap<>();
Scope(@Nullable Scope parent, Tree rootTree, PythonFile pythonFile, String fullyQualifiedModuleName) {
this.parent = parent;
this.rootTree = rootTree;
this.pythonFile = pythonFile;
this.fullyQualifiedModuleName = fullyQualifiedModuleName;
}
Set symbols() {
return Collections.unmodifiableSet(symbols);
}
void createBuiltinSymbol(String name, Map typeShedSymbols) {
SymbolImpl symbol;
Symbol typeShedSymbol = typeShedSymbols.get(name);
if (typeShedSymbol != null) {
symbol = ((SymbolImpl) typeShedSymbol).copyWithoutUsages();
} else {
symbol = new SymbolImpl(name, name);
}
if ("True".equals(name) || "False".equals(name)) {
symbol.setInferredType(InferredTypes.BOOL);
}
symbols.add(symbol);
builtinSymbols.add(symbol);
symbolsByName.put(name, symbol);
}
void createSymbolsFromWildcardImport(Set importedSymbols, ImportFrom importFrom, Map globalSymbolsByFQN) {
importedSymbols.forEach(symbol -> {
Symbol importedSymbol = copySymbol(symbol.name(), symbol, globalSymbolsByFQN);
if (!isExistingSymbol(importedSymbol.name())) {
symbols.add(importedSymbol);
symbolsByName.put(symbol.name(), importedSymbol);
((SymbolImpl) importedSymbol).addUsage(importFrom, Usage.Kind.IMPORT);
} else {
SymbolImpl originalSymbol = resolve(symbol.name());
if (originalSymbol != null) {
resetSymbolInfo(importedSymbol.fullyQualifiedName(), originalSymbol);
originalSymbol.addUsage(importFrom, Usage.Kind.IMPORT);
}
}
});
}
void createSelfParameter(Parameter parameter) {
Name nameTree = parameter.name();
if (nameTree == null) {
return;
}
String symbolName = nameTree.name();
SymbolImpl symbol = new SelfSymbolImpl(symbolName, parent);
symbols.add(symbol);
symbolsByName.put(symbolName, symbol);
symbol.addUsage(nameTree, Usage.Kind.PARAMETER);
}
void addFunctionSymbol(FunctionDef functionDef, @Nullable String fullyQualifiedName) {
String symbolName = functionDef.name().name();
if (isExistingSymbol(symbolName)) {
addBindingUsage(functionDef.name(), Usage.Kind.FUNC_DECLARATION, fullyQualifiedName);
} else {
FunctionSymbolImpl functionSymbol = new FunctionSymbolImpl(functionDef, fullyQualifiedName, pythonFile);
((FunctionDefImpl) functionDef).setFunctionSymbol(functionSymbol);
symbols.add(functionSymbol);
symbolsByName.put(symbolName, functionSymbol);
functionSymbol.addUsage(functionDef.name(), Usage.Kind.FUNC_DECLARATION);
}
}
private static Symbol copySymbol(String symbolName, Symbol symbol, Map globalSymbolsByFQN) {
if (symbol.is(Symbol.Kind.FUNCTION)) {
return new FunctionSymbolImpl(symbolName, (FunctionSymbol) symbol);
} else if (symbol.is(Symbol.Kind.CLASS)) {
ClassSymbolImpl originalClassSymbol = (ClassSymbolImpl) symbol;
// Must use symbolName to preserve import aliases
ClassSymbolImpl classSymbol = new ClassSymbolImpl(symbolName, originalClassSymbol.fullyQualifiedName(), originalClassSymbol.definitionLocation());
for (Symbol originalSymbol : originalClassSymbol.superClasses()) {
Symbol globalSymbol = globalSymbolsByFQN.get(originalSymbol.fullyQualifiedName());
if (globalSymbol != null && globalSymbol.kind() == Symbol.Kind.CLASS) {
classSymbol.addSuperClass(copySymbol(globalSymbol.name(), globalSymbol, globalSymbolsByFQN));
} else {
classSymbol.addSuperClass(originalSymbol);
}
}
classSymbol.addMembers(originalClassSymbol
.declaredMembers().stream()
.map(m -> ((SymbolImpl) m).copyWithoutUsages())
.collect(Collectors.toList()));
if (originalClassSymbol.hasSuperClassWithoutSymbol()) {
classSymbol.setHasSuperClassWithoutSymbol();
}
return classSymbol;
} else if (symbol.is(Symbol.Kind.AMBIGUOUS)) {
Set alternativeSymbols = ((AmbiguousSymbol) symbol).alternatives().stream()
.map(s -> copySymbol(symbolName, s, globalSymbolsByFQN))
.collect(Collectors.toSet());
return AmbiguousSymbolImpl.create(alternativeSymbols);
} else if (symbol.is(Symbol.Kind.OTHER)) {
SymbolImpl res = new SymbolImpl(symbolName, symbol.fullyQualifiedName());
for (Map.Entry kv: ((SymbolImpl) symbol).getChildrenSymbolByName().entrySet()) {
res.addChildSymbol(((SymbolImpl) kv.getValue()).copyWithoutUsages());
}
return res;
}
return new SymbolImpl(symbolName, symbol.fullyQualifiedName());
}
void addModuleSymbol(Name nameTree, @CheckForNull String fullyQualifiedName, Map> globalSymbolsByModuleName, Map globalSymbolsByFQN) {
String symbolName = nameTree.name();
Set moduleExportedSymbols = globalSymbolsByModuleName.get(fullyQualifiedName);
if (moduleExportedSymbols != null && !isExistingSymbol(symbolName)) {
SymbolImpl moduleSymbol = new SymbolImpl(symbolName, fullyQualifiedName);
moduleExportedSymbols.forEach(symbol -> moduleSymbol.addChildSymbol(copySymbol(symbol.name(), symbol, globalSymbolsByFQN)));
this.symbols.add(moduleSymbol);
symbolsByName.put(symbolName, moduleSymbol);
} else if (!isExistingSymbol(symbolName) && fullyQualifiedName != null && !fullyQualifiedName.equals(fullyQualifiedModuleName) && !isTypeShedFile(pythonFile)) {
Set standardLibrarySymbols = TypeShed.symbolsForModule(fullyQualifiedName);
if (!standardLibrarySymbols.isEmpty()) {
SymbolImpl moduleSymbol = new SymbolImpl(symbolName, fullyQualifiedName);
standardLibrarySymbols.forEach(symbol -> moduleSymbol.addChildSymbol(copySymbol(symbol.name(), symbol, globalSymbolsByFQN)));
this.symbols.add(moduleSymbol);
symbolsByName.put(symbolName, moduleSymbol);
}
}
addBindingUsage(nameTree, Usage.Kind.IMPORT, fullyQualifiedName);
}
void addImportedSymbol(Name nameTree, @CheckForNull String fullyQualifiedName, String fromModuleName, Map globalSymbolsByFQN) {
String symbolName = nameTree.name();
Symbol globalSymbol = globalSymbolsByFQN.get(fullyQualifiedName);
if (globalSymbol == null && fullyQualifiedName != null && !fromModuleName.equals(fullyQualifiedModuleName) && !isTypeShedFile(pythonFile)) {
//FIXME: Resolve imports from TypeShed files without trying to resolve cyclic dependencies
globalSymbol = TypeShed.symbolWithFQN(fromModuleName, fullyQualifiedName);
}
if (globalSymbol == null || isExistingSymbol(symbolName)) {
addBindingUsage(nameTree, Usage.Kind.IMPORT, fullyQualifiedName);
} else {
Symbol symbol = copySymbol(symbolName, globalSymbol, globalSymbolsByFQN);
this.symbols.add(symbol);
symbolsByName.put(symbolName, symbol);
((SymbolImpl) symbol).addUsage(nameTree, Usage.Kind.IMPORT);
}
}
private boolean isExistingSymbol(String symbolName) {
return symbolsByName.containsKey(symbolName) || globalNames.contains(symbolName) || nonlocalNames.contains(symbolName);
}
void addBindingUsage(Name nameTree, Usage.Kind kind, @Nullable String fullyQualifiedName) {
String symbolName = nameTree.name();
if (!isExistingSymbol(symbolName)) {
SymbolImpl symbol = new SymbolImpl(symbolName, fullyQualifiedName);
symbols.add(symbol);
symbolsByName.put(symbolName, symbol);
}
SymbolImpl symbol = resolve(symbolName);
if (symbol != null) {
resetSymbolInfo(fullyQualifiedName, symbol);
symbol.addUsage(nameTree, kind);
}
}
private static void resetSymbolInfo(@Nullable String fullyQualifiedName, SymbolImpl symbol) {
if (!Symbol.Kind.OTHER.equals(symbol.kind())) {
symbol.setKind(Symbol.Kind.OTHER);
}
if (fullyQualifiedName != null && !fullyQualifiedName.equals(symbol.fullyQualifiedName)) {
symbol.fullyQualifiedName = null;
}
if (fullyQualifiedName == null && symbol.fullyQualifiedName != null) {
symbol.fullyQualifiedName = null;
}
}
@CheckForNull
SymbolImpl resolve(String symbolName) {
Symbol symbol = symbolsByName.get(symbolName);
if (parent == null || symbol != null) {
return (SymbolImpl) symbol;
}
if (parent.rootTree.is(Tree.Kind.CLASSDEF)) {
return parent.parent.resolve(symbolName);
}
return parent.resolve(symbolName);
}
void addGlobalName(String name) {
globalNames.add(name);
}
void addNonLocalName(String name) {
nonlocalNames.add(name);
}
void addClassSymbol(ClassDef classDef, @Nullable String fullyQualifiedName) {
String symbolName = classDef.name().name();
if (isExistingSymbol(symbolName)) {
addBindingUsage(classDef.name(), Usage.Kind.CLASS_DECLARATION, fullyQualifiedName);
} else {
ClassSymbolImpl classSymbol = new ClassSymbolImpl(classDef, fullyQualifiedName, pythonFile);
symbols.add(classSymbol);
symbolsByName.put(symbolName, classSymbol);
classSymbol.addUsage(classDef.name(), Usage.Kind.CLASS_DECLARATION);
}
}
void replaceSymbolWithAmbiguousSymbol(Symbol symbol, AmbiguousSymbol ambiguousSymbol) {
symbols.remove(symbol);
symbols.add(ambiguousSymbol);
symbolsByName.remove(symbol.name());
symbolsByName.put(symbol.name(), ambiguousSymbol);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy