/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.tvl.goworks.editor.go.completion;
import com.tvl.spi.editor.completion.CompletionItem;
import com.tvl.spi.editor.completion.CompletionProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.Document;
import org.antlr.netbeans.editor.classification.TokenTag;
import org.antlr.netbeans.editor.completion.Anchor;
import org.antlr.netbeans.editor.tagging.Tagger;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.netbeans.editor.text.DocumentTextUtilities;
import org.antlr.netbeans.editor.text.OffsetRegion;
import org.antlr.netbeans.editor.text.SnapshotPosition;
import org.antlr.netbeans.editor.text.SnapshotPositionRegion;
import org.antlr.netbeans.editor.text.TrackingPositionRegion;
import org.antlr.netbeans.editor.text.VersionedDocument;
import org.antlr.netbeans.editor.text.VersionedDocumentUtilities;
import org.antlr.netbeans.parsing.spi.ParserData;
import org.antlr.netbeans.parsing.spi.ParserDataOptions;
import org.antlr.netbeans.parsing.spi.ParserTaskManager;
import org.antlr.netbeans.semantics.ObjectDecorator;
import org.antlr.netbeans.semantics.ObjectProperty;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Dependents;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.RuleDependencies;
import org.antlr.v4.runtime.RuleDependency;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenSource;
import org.antlr.v4.runtime.atn.ATNConfig;
import org.antlr.v4.runtime.atn.NotSetTransition;
import org.antlr.v4.runtime.atn.Transition;
import org.antlr.v4.runtime.atn.WildcardTransition;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.Tuple;
import org.antlr.v4.runtime.misc.Tuple3;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.antlr.works.editor.antlr4.classification.TaggerTokenSource;
import org.antlr.works.editor.antlr4.completion.AbstractCompletionQuery;
import org.antlr.works.editor.antlr4.completion.CaretReachedException;
import org.antlr.works.editor.antlr4.completion.CaretToken;
import org.antlr.works.editor.antlr4.completion.CodeCompletionErrorStrategy;
import org.antlr.works.editor.antlr4.completion.CodeCompletionTokenSource;
import org.antlr.works.editor.antlr4.parsing.ParseTrees;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.editor.BaseDocument;
import org.openide.util.Parameters;
import org.tvl.goworks.editor.go.GoParserDataDefinitions;
import org.tvl.goworks.editor.go.codemodel.ChannelKind;
import org.tvl.goworks.editor.go.codemodel.CodeElementModel;
import org.tvl.goworks.editor.go.codemodel.CodeElementPositionRegion;
import org.tvl.goworks.editor.go.codemodel.CodeModelCache;
import org.tvl.goworks.editor.go.codemodel.ConstModel;
import org.tvl.goworks.editor.go.codemodel.FileModel;
import org.tvl.goworks.editor.go.codemodel.FunctionModel;
import org.tvl.goworks.editor.go.codemodel.ImportDeclarationModel;
import org.tvl.goworks.editor.go.codemodel.IntrinsicTypeModels;
import org.tvl.goworks.editor.go.codemodel.PackageModel;
import org.tvl.goworks.editor.go.codemodel.TypeAliasModel;
import org.tvl.goworks.editor.go.codemodel.TypeArrayModel;
import org.tvl.goworks.editor.go.codemodel.TypeKind;
import org.tvl.goworks.editor.go.codemodel.TypeMapModel;
import org.tvl.goworks.editor.go.codemodel.TypeModel;
import org.tvl.goworks.editor.go.codemodel.TypePointerModel;
import org.tvl.goworks.editor.go.codemodel.TypeSliceModel;
import org.tvl.goworks.editor.go.codemodel.TypeWrapperModel;
import org.tvl.goworks.editor.go.codemodel.VarKind;
import org.tvl.goworks.editor.go.codemodel.VarModel;
import org.tvl.goworks.editor.go.codemodel.impl.AbstractCodeElementModel;
import org.tvl.goworks.editor.go.codemodel.impl.CodeModelCacheImpl;
import org.tvl.goworks.editor.go.codemodel.impl.ConstModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.FileModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.FunctionModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.ParameterModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeArrayModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeChannelModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeFunctionModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeInterfaceModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeMapModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypePointerModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeSliceModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypeWrapperModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.VarModelImpl;
import org.tvl.goworks.editor.go.highlighter.SemanticHighlighter;
import org.tvl.goworks.editor.go.parser.GoLexer;
import org.tvl.goworks.editor.go.parser.GoParser;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.AddExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.AndExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ArrayTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BaseTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BaseTypeNameContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BasicLiteralContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BlockContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BuiltinArgsContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BuiltinCallContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.BuiltinCallExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.CallExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ChannelTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.CommClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.CompositeLiteralContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ConstDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ConstSpecContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ConversionContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ConversionOrCallExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.DeclarationContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ElementNameOrIndexContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ElementTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ExprCaseClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ExprSwitchStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ExpressionContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.FieldDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ForClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ForStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.FunctionDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.FunctionLiteralContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.FunctionTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.IdentifierListContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.IfStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ImportDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ImportSpecContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.IndexExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.InitStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.InterfaceTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.KeyTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.LabelContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.LabeledStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.LiteralContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.LiteralTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.MapTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.MethodDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.MethodNameContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.MethodSpecContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.MultExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.OperandContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.OperandExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.OrExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.PackageClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.PackageNameContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ParameterDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ParameterListContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ParametersContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.PointerTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.PostStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.QualifiedIdentifierContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.RangeClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ReceiverContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.RecvStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ResultContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SelectStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SelectorExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.ShortVarDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SignatureContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SimpleStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SliceExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SliceTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.SourceFileBodyContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.StatementContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.StructTypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TopLevelDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeAssertionExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeCaseClauseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeListContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeLiteralContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeNameContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeSpecContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeSwitchCaseContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeSwitchGuardContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.TypeSwitchStmtContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.UnaryExprContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.VarDeclContext;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser.VarSpecContext;
import org.tvl.goworks.editor.go.parser.generated.GoParserBaseListener;
import org.tvl.goworks.editor.go.parser.generated.GoParserBaseVisitor;
import org.tvl.goworks.editor.go.semantics.BundledReturnTypeModel;
import org.tvl.goworks.editor.go.semantics.CodeElementReference;
import org.tvl.goworks.editor.go.semantics.GoAnnotatedParseTree;
import org.tvl.goworks.editor.go.semantics.GoAnnotations;
import org.tvl.goworks.editor.go.semantics.NodeType;
import org.tvl.goworks.editor.go.semantics.SemanticAnalyzer;
import org.tvl.goworks.project.GoProject;
/**
*
* @author Sam Harwell
*/
public final class GoCompletionQuery extends AbstractCompletionQuery {
// -J-Dorg.tvl.goworks.editor.go.completion.GoCompletionQuery.level=FINE
private static final Logger LOGGER = Logger.getLogger(GoCompletionQuery.class.getName());
private boolean possibleReference;
/*package*/ GoCompletionQuery(GoCompletionProvider completionProvider, int queryType, int caretOffset, boolean hasTask, boolean extend) {
super(completionProvider, queryType, caretOffset, hasTask, extend);
}
public static boolean isInContext(Parser parser, RuleContext context, IntervalSet values) {
return isInContext(parser, context, values, true);
}
public static boolean isInContext(Parser parser, RuleContext context, IntervalSet values, boolean checkTop) {
return getTopContext(parser, context, values, checkTop) != null;
}
public static boolean isInContext(ParserRuleContext context, IntervalSet values) {
return isInContext(context, values, true);
}
public static boolean isInContext(ParserRuleContext context, IntervalSet values, boolean checkTop) {
return getTopContext(context, values, checkTop) != null;
}
public static RuleContext getTopContext(Parser parser, RuleContext context, IntervalSet values) {
return getTopContext(parser, context, values, true);
}
public static RuleContext getTopContext(Parser parser, RuleContext context, IntervalSet values, boolean checkTop) {
if (checkTop && context instanceof ParserRuleContext) {
if (values.contains(context.getRuleIndex())) {
return context;
}
}
if (context.isEmpty()) {
return null;
}
if (values.contains(parser.getATN().states.get(context.invokingState).ruleIndex)) {
return context.parent;
}
return getTopContext(parser, context.parent, values, false);
}
@CheckForNull
public static ParseTree getTopContext(@NonNull ParseTree context, @NonNull IntervalSet values) {
return getTopContext(context, values, true);
}
@CheckForNull
public static ParseTree getTopContext(@NonNull ParseTree context, @NonNull IntervalSet values, boolean checkTop) {
if (checkTop && context instanceof RuleNode && values.contains(((RuleNode)context).getRuleContext().getRuleIndex())) {
return context;
}
ParseTree parent = context.getParent();
if (parent == null) {
return null;
}
return getTopContext(parent, values, true);
}
@Override
public GoCompletionProvider getCompletionProvider() {
return (GoCompletionProvider)super.getCompletionProvider();
}
@Override
protected boolean isQueryContext(Document doc, int caretOffset) {
return getCompletionProvider().isGoContext(getComponent(), caretOffset, true);
}
@Override
protected Task getTask(BaseDocument document) {
VersionedDocument buffer = VersionedDocumentUtilities.getVersionedDocument(document);
DocumentSnapshot snapshot = buffer.getCurrentSnapshot();
return new TaskImpl(document, snapshot);
}
/*package*/ static boolean isIdentifierPart(String typedText) {
for (int i = 0; i < typedText.length(); i++) {
if (!Character.isJavaIdentifierPart(typedText.charAt(i))) {
return false;
}
}
return true;
}
@Override
protected CompletionItem createDeclarationCompletionItem(Document document, TrackingPositionRegion applicableTo) {
return new DeclarationCompletionItem(document, applicableTo);
}
private static final ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> ATTR_CONSTANTS = new ObjectProperty<>("constants");
private static final ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> ATTR_LOCALS = new ObjectProperty<>("locals");
private static final ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> ATTR_PARAMETER = new ObjectProperty<>("parameter");
private static final ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> ATTR_RECEIVER_PARAMETER = new ObjectProperty<>("receiver-parameter");
private static final ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> ATTR_RETURN_PARAMETER = new ObjectProperty<>("return-parameter");
private static final ObjectProperty<Collection<? extends CodeElementModel>> ATTR_TARGET = new ObjectProperty<>("target");
@RuleDependencies({
// these are dependencies from the BREAK_SCOPES and CONTINUE_SCOPES fields
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_forStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_switchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_selectStmt, version=0),
})
private class TaskImpl extends Task {
private final DocumentSnapshot snapshot;
private final ParserTaskManager taskManager;
private FileModelImpl fileModel;
private boolean fileModelDataFailed = false;
private CodeCompletionGoParser parser;
private LocalsAnalyzer localsAnalyzer = new LocalsAnalyzer();
private TargetAnalyzer targetAnalyzer = new TargetAnalyzer();
private final IntervalSet BREAK_SCOPES = new IntervalSet() {{
add(GoParser.RULE_forStmt);
add(GoParser.RULE_switchStmt);
add(GoParser.RULE_selectStmt);
}};
private final IntervalSet CONTINUE_SCOPES = IntervalSet.of(GoParser.RULE_forStmt);
public TaskImpl(BaseDocument document, DocumentSnapshot snapshot) {
super(document);
Parameters.notNull("snapshot", snapshot);
this.snapshot = snapshot;
this.taskManager = getParserTaskManager();
if (taskManager == null) {
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_topLevelDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_label, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseTypeName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_builtinCall, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_receiver, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchGuard, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchCase, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_exprSwitchCase, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_commCase, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodExpr, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_compositeLiteral, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementNameOrIndex, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_operand, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_literalValue, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeName, version=3, dependents=Dependents.PARENTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_labeledStmt, version=0),
})
public void runImpl(BaseDocument document) {
results = new ArrayList<>();
possibleDeclaration = true;
possibleReference = true;
// Add context items (labels, etc). Use anchor points to optimize information gathering.
Map<RuleContext, CaretReachedException> parseTrees;
Map<ParseTree, GoAnnotatedParseTree> annotatedParseTrees;
CaretToken caretToken = null;
List<Anchor> anchors;
Future<ParserData<List<Anchor>>> result =
taskManager.getData(snapshot, GoParserDataDefinitions.DYNAMIC_ANCHOR_POINTS, EnumSet.of(ParserDataOptions.SYNCHRONOUS));
try {
anchors = result != null ? result.get().getData() : null;
} catch (InterruptedException ex) {
anchors = null;
} catch (ExecutionException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while getting anchor points.", ex);
anchors = null;
}
if (anchors != null) {
Anchor enclosing = null;
Anchor previous = null;
Anchor next = null;
/*
* parse the current rule
*/
for (Anchor anchor : anchors) {
// TODO: support more anchors
if (anchor.getRule() != GoParser.RULE_topLevelDecl) {
continue;
}
if (anchor.getSpan().getStartPosition(snapshot).getOffset() <= getCaretOffset()) {
previous = anchor;
if (anchor.getSpan().getEndPosition(snapshot).getOffset() > getCaretOffset()) {
enclosing = anchor;
}
} else {
next = anchor;
break;
}
}
Future<ParserData<Tagger<TokenTag<Token>>>> futureTokensData = taskManager.getData(snapshot, GoParserDataDefinitions.LEXER_TOKENS, EnumSet.of(ParserDataOptions.SYNCHRONOUS));
Tagger<TokenTag<Token>> tagger = null;
try {
tagger = futureTokensData != null ? futureTokensData.get().getData() : null;
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while getting tokens.", ex);
}
int regionStart;
if (enclosing != null) {
regionStart = enclosing.getSpan().getStartPosition(snapshot).getOffset();
} else if (previous != null) {
regionStart = previous.getSpan().getStartPosition(snapshot).getOffset();
} else {
regionStart = 0;
}
int regionEnd = Math.min(snapshot.length(), getCaretOffset() + 1);
OffsetRegion region = OffsetRegion.fromBounds(regionStart, regionEnd);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Code completion from {0}: {1}.", new Object[] { enclosing != null || previous != null ? "anchor region" : "top of File", region });
}
TaggerTokenSource taggerTokenSource = new TaggerTokenSource(tagger, new SnapshotPositionRegion(snapshot, region));
TokenSource tokenSource = new CodeCompletionTokenSource(getCaretOffset(), taggerTokenSource);
CommonTokenStream tokens = new CommonTokenStream(tokenSource);
parser = ParserFactory.DEFAULT.getParser(tokens);
try {
parser.setBuildParseTree(true);
parser.setErrorHandler(new CodeCompletionErrorStrategy());
@SuppressWarnings("LocalVariableHidesMemberVariable")
FileModel fileModel = getFileModel();
if (fileModel != null) {
Set<String> packageNames = new HashSet<>();
for (ImportDeclarationModel model : fileModel.getImportDeclarations()) {
String name = model.getName();
if (!name.isEmpty() && !name.equals(".")) {
packageNames.add(name);
}
}
parser.setCheckPackageNames(true);
parser.setPackageNames(packageNames);
}
int anchorRule;
if (enclosing != null) {
anchorRule = enclosing.getRule();
} else if (previous != null) {
anchorRule = previous.getRule();
} else {
anchorRule = GoParser.RULE_topLevelDecl;
}
switch (anchorRule) {
case GoParser.RULE_topLevelDecl:
parseTrees = GoForestParser.INSTANCE.getParseTrees(parser);
annotatedParseTrees = new HashMap<>();
break;
default:
parseTrees = null;
annotatedParseTrees = null;
break;
}
if (parseTrees != null) {
possibleDeclaration = false;
possibleReference = false;
declarationOrReferenceLoop:
for (Map.Entry<RuleContext, CaretReachedException> entry : parseTrees.entrySet()) {
CaretReachedException ex = entry.getValue();
if (ex == null || ex.getTransitions() == null) {
continue;
}
if (ex.getCaretToken() != null) {
caretToken = ex.getCaretToken();
}
Map<ATNConfig, List<Transition>> transitions = entry.getValue().getTransitions();
for (Map.Entry<ATNConfig, List<Transition>> transitionEntry : transitions.entrySet()) {
for (Transition t : transitionEntry.getValue()) {
IntervalSet label = t.label();
if (label == null || !label.contains(GoParser.IDENTIFIER)) {
continue;
}
int ruleIndex = t.target.ruleIndex;
switch (ruleIndex) {
case GoParser.RULE_methodName:
{
int invokingRule = ParseTrees.getInvokingRule(parser.getATN(), entry.getValue().getFinalContext());
if (invokingRule == GoParser.RULE_methodSpec || invokingRule == GoParser.RULE_methodDecl) {
possibleDeclaration = true;
} else {
possibleReference = true;
}
}
break;
case GoParser.RULE_label:
{
int invokingRule = ParseTrees.getInvokingRule(parser.getATN(), entry.getValue().getFinalContext());
if (invokingRule == GoParser.RULE_labeledStmt) {
possibleDeclaration = true;
} else {
possibleReference = true;
}
}
break;
case GoParser.RULE_packageName:
if (isInContext(parser, entry.getValue().getFinalContext(), IntervalSet.of(GoParser.RULE_packageClause))) {
possibleDeclaration = true;
} else {
possibleReference = true;
}
break;
case GoParser.RULE_baseTypeName:
case GoParser.RULE_builtinCall: // only happens for builtin method name
case GoParser.RULE_expression: // only happens for selector
case GoParser.RULE_qualifiedIdentifier:
possibleReference = true;
break;
case GoParser.RULE_identifierList:
case GoParser.RULE_functionDecl:
case GoParser.RULE_receiver:
case GoParser.RULE_typeSpec:
case GoParser.RULE_typeSwitchGuard:
possibleDeclaration = true;
break;
default:
break;
}
if (possibleDeclaration && possibleReference) {
break;
}
}
}
}
}
Map<String, CompletionItem> intermediateResults = new HashMap<>();
if (parseTrees != null) {
/*
* KEYWORD ANALYSIS
*/
boolean canContinue = false;
boolean canBreak = false;
boolean canDefaultOrCase = false;
IntervalSet allowedKeywords = new IntervalSet();
IntervalSet remainingKeywords = new IntervalSet(KeywordCompletionItem.KEYWORD_TYPES);
for (Map.Entry<RuleContext, CaretReachedException> entry : parseTrees.entrySet()) {
CaretReachedException caretReachedException = entry.getValue();
if (caretReachedException == null || caretReachedException.getTransitions() == null) {
continue;
}
Map<ATNConfig, List<Transition>> transitions = caretReachedException.getTransitions();
for (List<Transition> transitionList : transitions.values()) {
for (Transition transition : transitionList) {
if (transition.isEpsilon() || transition instanceof WildcardTransition || transition instanceof NotSetTransition) {
continue;
}
IntervalSet label = transition.label();
if (label == null) {
continue;
}
if (!canContinue && label.contains(GoLexer.Continue)) {
canContinue = isInContext(parser, caretReachedException.getFinalContext(), CONTINUE_SCOPES);
if (canContinue) {
canBreak = true;
}
}
if (!canBreak && label.contains(GoLexer.Break)) {
canBreak = isInContext(parser, caretReachedException.getFinalContext(), BREAK_SCOPES);
}
if (!canDefaultOrCase && label.contains(GoLexer.Default)) {
if (caretReachedException.getFinalContext() instanceof ParserRuleContext) {
int currentRule = caretReachedException.getFinalContext().getRuleIndex();
canDefaultOrCase =
currentRule == GoParser.RULE_typeSwitchCase
|| currentRule == GoParser.RULE_exprSwitchCase
|| currentRule == GoParser.RULE_commCase;
} else {
canDefaultOrCase = true;
}
}
for (int keyword : remainingKeywords.toArray()) {
if (label.contains(keyword)) {
remainingKeywords.remove(keyword);
allowedKeywords.add(keyword);
}
}
}
}
}
if (!canContinue) {
allowedKeywords.remove(GoLexer.Continue);
}
if (!canBreak) {
allowedKeywords.remove(GoLexer.Break);
}
if (!canDefaultOrCase) {
allowedKeywords.remove(GoLexer.Case);
allowedKeywords.remove(GoLexer.Default);
}
for (int keyword : allowedKeywords.toArray()) {
String literalName = GoLexer.VOCABULARY.getLiteralName(keyword);
if (literalName == null) {
continue;
}
KeywordCompletionItem item = new KeywordCompletionItem(literalName.substring(1, literalName.length() - 1));
intermediateResults.put(item.getInsertPrefix().toString(), item);
}
/*
* EXPRESSION ANALYSIS
*/
boolean addedPackages = false;
boolean addedTypes = false;
boolean addedVars = false;
boolean addedFunctions = false;
for (Map.Entry<RuleContext, CaretReachedException> entry : parseTrees.entrySet()) {
RuleContext finalContext = entry.getValue() != null ? entry.getValue().getFinalContext() : null;
if (finalContext == null) {
continue;
}
Map<ATNConfig, List<Transition>> transitions = entry.getValue().getTransitions();
assert transitions != null;
assert transitions.size() == 1;
List<Transition> singleTransitionList = transitions.values().iterator().next();
assert singleTransitionList.size() == 1;
Transition transition = singleTransitionList.get(0);
// only interested in identifiers
IntervalSet label = transition.label();
if (label == null || !label.contains(GoParser.IDENTIFIER)) {
continue;
}
// if selectorExpressionRoot is not null, then we are on the right hand side of a '.'
ParseTree selectorExpressionRoot = null;
ParserRuleContext selectorTarget = null;
boolean isTypeSwitchGuard = false;
List<ParseTree> pathToRoot = new ArrayList<ParseTree>(ParseTrees.getAncestors(finalContext));
pathToRoot.add(finalContext);
Collections.reverse(pathToRoot);
for (ParseTree tree : pathToRoot) {
if (tree instanceof GoParser.SelectorExprContext) {
GoParser.SelectorExprContext context = (GoParser.SelectorExprContext)tree;
if (context.dot != null) {
selectorExpressionRoot = tree;
selectorTarget = context.expression();
break;
}
} else if (tree instanceof GoParser.TypeSwitchGuardContext) {
GoParser.TypeSwitchGuardContext context = (GoParser.TypeSwitchGuardContext)tree;
if (context.dot != null) {
selectorExpressionRoot = tree;
selectorTarget = context.expression();
break;
}
} else if (tree instanceof GoParser.TypeAssertionExprContext) {
GoParser.TypeAssertionExprContext context = (GoParser.TypeAssertionExprContext)tree;
if (context.dot != null && context.lp == null) {
selectorExpressionRoot = tree;
selectorTarget = context.expression();
break;
}
} else if (tree instanceof GoParser.MethodExprContext) {
GoParser.MethodExprContext context = (GoParser.MethodExprContext)tree;
if (context.dot != null) {
selectorExpressionRoot = tree;
selectorTarget = context.receiverType();
break;
}
} else if (tree instanceof GoParser.QualifiedIdentifierContext) {
GoParser.QualifiedIdentifierContext context = (GoParser.QualifiedIdentifierContext)tree;
if (context.dot != null) {
selectorExpressionRoot = tree;
selectorTarget = context.packageName();
break;
}
}
}
if (selectorExpressionRoot != null) {
/*
* SELECTOR EXPRESSION
*/
if (isTypeSwitchGuard) {
intermediateResults.put("(type)", new KeywordCompletionItem("(type)"));
continue;
}
if (getFileModel() == null) {
continue;
}
final boolean INCLUDE_EXPRESSION = false;
if (INCLUDE_EXPRESSION) {
String text = tokens.getText(selectorTarget.start, selectorTarget.stop);
if (text != null && !text.isEmpty()) {
intermediateResults.put(text, new KeywordCompletionItem(text));
}
}
if (annotatedParseTrees == null) {
continue;
}
GoAnnotatedParseTree annotatedParseTree = getAnnotatedParseTree(snapshot.getVersionedDocument(), entry.getKey(), annotatedParseTrees);
PackageModel currentPackage = getFileModel().getPackage();
Map<String, Collection<PackageModel>> resolvedPackages = new HashMap<>();
for (ImportDeclarationModel importDeclarationModel : getFileModel().getImportDeclarations()) {
Collection<PackageModel> packages = resolvedPackages.get(importDeclarationModel.getName());
if (packages == null) {
packages = new ArrayList<>();
resolvedPackages.put(importDeclarationModel.getName(), packages);
}
packages.addAll(CodeModelCacheImpl.getInstance().getPackages(getFileModel().getPackage().getProject(), importDeclarationModel.getPath()));
GoProject mainProject = getFileModel().getPackage().getProject();
for (GoProject library : mainProject.getLibraryProjects()) {
packages.addAll(CodeModelCacheImpl.getInstance().getPackages(library, importDeclarationModel.getPath()));
}
}
Collection<? extends CodeElementModel> models = resolveSelectorTarget(selectorTarget, annotatedParseTree, currentPackage, resolvedPackages);
assert models != null;
for (CodeElementModel model : models) {
Collection<? extends CodeElementModel> selected = SemanticAnalyzer.getSelectableMembers(model);
for (CodeElementModel selectedModel : selected) {
if (selectedModel instanceof FunctionModel) {
FunctionModel function = (FunctionModel)selectedModel;
intermediateResults.put(function.getName(), new FunctionReferenceCompletionItem(function));
} else if (selectedModel instanceof ConstModel) {
ConstModel function = (ConstModel)selectedModel;
intermediateResults.put(function.getName(), new ConstReferenceCompletionItem(function, false));
} else if (selectedModel instanceof VarModel) {
VarModel function = (VarModel)selectedModel;
intermediateResults.put(function.getName(), new VarReferenceCompletionItem(function, false));
} else if (selectedModel instanceof TypeModel) {
TypeModel function = (TypeModel)selectedModel;
intermediateResults.put(function.getName(), new TypeReferenceCompletionItem(function));
} else {
LOGGER.log(Level.FINE, "TODO: Unknown model '{0}'.", model.getClass().getSimpleName());
}
}
//if (model instanceof PackageModel) {
// PackageModel packageModel = (PackageModel)model;
// if (selectorExpressionRoot instanceof GoParser.methodExprContext) {
// continue;
// }
//
// for (FunctionModel function : packageModel.getFunctions()) {
// intermediateResults.put(function.getName(), new FunctionReferenceCompletionItem(function));
// }
//
// for (ConstModel constant : packageModel.getConstants()) {
// intermediateResults.put(constant.getName(), new ConstReferenceCompletionItem(constant, false));
// }
//
// for (VarModel var : packageModel.getVars()) {
// intermediateResults.put(var.getName(), new VarReferenceCompletionItem(var, false));
// }
//
// for (TypeModel type : packageModel.getTypes()) {
// intermediateResults.put(type.getName(), new TypeReferenceCompletionItem(type));
// }
//} else if (model instanceof TypeModel) {
// TypeModel typeModel = (TypeModel)model;
// if (selectorExpressionRoot instanceof GoParser.methodExprContext) {
// for (FunctionModel method : typeModel.getMethods()) {
// intermediateResults.put(method.getName(), new FunctionReferenceCompletionItem(method));
// }
// continue;
// }
//
// // TODO: any other possibilities?
//} else if (model instanceof VarModel) {
// VarModel varModel = (VarModel)model;
// if (selectorExpressionRoot instanceof GoParser.methodExprContext) {
// continue;
// }
//
// TypeModel varType = varModel.getVarType();
// for (FunctionModel method : varType.getMethods()) {
// intermediateResults.put(method.getName(), new FunctionReferenceCompletionItem(method));
// }
//
// for (VarModel var : varType.getFields()) {
// intermediateResults.put(var.getName(), new VarReferenceCompletionItem(var, false));
// }
//} else {
// LOGGER.log(Level.FINE, "TODO: Unknown model '{0}'.", model.getClass().getSimpleName());
//}
}
} else if (finalContext instanceof GoParser.PackageNameContext) {
/* AVAILABLE PACKAGES
*/
if (getFileModel() == null || addedPackages) {
continue;
}
addedPackages = true;
Collection<? extends ImportDeclarationModel> imports = getFileModel().getImportDeclarations();
for (ImportDeclarationModel model : imports) {
if (intermediateResults.containsKey(model.getName())) {
continue;
}
intermediateResults.put(model.getName(), new PackageReferenceCompletionItem(model.getName()));
}
} else {
/*
* UNQUALIFIED EXPRESSION. Identifier could reference any visible:
* - type
* - var
* - function
* - method
*
* Visible means located in any of the following:
* - the current package
* - any packages imported with alias '.'
* - built-in elements
*/
RuleContext compositeLiteralRuleContext = getTopContext(parser, finalContext, IntervalSet.of(GoParser.RULE_compositeLiteral));
GoParser.CompositeLiteralContext compositeLiteralContext = (GoParser.CompositeLiteralContext)compositeLiteralRuleContext;
if (finalContext instanceof GoParser.QualifiedIdentifierContext) {
GoParser.QualifiedIdentifierContext context = (GoParser.QualifiedIdentifierContext)finalContext;
if (context.packageName() != null) {
continue;
}
if (context.getParent() instanceof OperandContext
&& context.getParent().getParent() instanceof ExpressionContext
&& context.getParent().getParent().getParent() instanceof ElementNameOrIndexContext)
{
// could be a field name
assert compositeLiteralContext != null && compositeLiteralContext.literalType() != null;
List<GoParser.LiteralValueContext> literalValueContexts = new ArrayList<GoParser.LiteralValueContext>();
for (RuleContext context2 = finalContext; context2 != null; context2 = context2.parent) {
if (context2 instanceof GoParser.LiteralValueContext) {
literalValueContexts.add((GoParser.LiteralValueContext)context2);
} else if (context2 instanceof GoParser.CompositeLiteralContext) {
// stop at the containing compositeLiteral
break;
}
}
// first resolve the type of the containing compositeLiteral
Collection<? extends CodeElementModel> models = targetAnalyzer.visit(compositeLiteralContext.literalType());
if (literalValueContexts.size() > 1) {
LOGGER.log(Level.FINE, "TODO: resolve nested values - need type of the innermost value.");
models = Collections.emptyList();
}
if (!models.isEmpty()) {
for (CodeElementModel model : models) {
if (!(model instanceof TypeModel)) {
continue;
}
TypeModel typeModel = (TypeModel)model;
for (VarModel field : typeModel.getFields()) {
String key = field.getName() + ":";
if (intermediateResults.containsKey(key)) {
continue;
}
intermediateResults.put(key, new FieldReferenceKeyCompletionItem(field));
}
}
}
continue;
}
if (compositeLiteralContext != null && compositeLiteralContext.literalValue() != null) {
LOGGER.log(Level.FINE, "TODO: is there any other work to do for this case?");
}
// add items from the current package and imported "mergeWithLocal" packages
List<PackageModel> visiblePackages = new ArrayList<>();
visiblePackages.add(getFileModel().getPackage());
for (ImportDeclarationModel importDeclarationModel : getFileModel().getImportDeclarations()) {
if (importDeclarationModel.isMergeWithLocal()) {
Collection<? extends PackageModel> resolved = CodeModelCacheImpl.getInstance().resolvePackages(importDeclarationModel);
if (resolved != null) {
visiblePackages.addAll(resolved);
}
}
}
if (finalContext.getParent() instanceof GoParser.TypeNameContext) {
for (String builtin : SemanticHighlighter.PREDEFINED_TYPES) {
if (intermediateResults.containsKey(builtin)) {
continue;
}
intermediateResults.put(builtin, new TypeReferenceCompletionItem(builtin));
}
for (PackageModel packageModel : visiblePackages) {
Collection<? extends TypeModel> types = packageModel.getTypes();
for (TypeModel model : types) {
if (intermediateResults.containsKey(model.getName())) {
continue;
}
intermediateResults.put(model.getName(), new TypeReferenceCompletionItem(model));
}
}
} else {
assert finalContext.getParent() instanceof GoParser.OperandContext;
// add builtin items
for (String builtin : SemanticHighlighter.PREDEFINED_CONSTANTS) {
if (intermediateResults.containsKey(builtin)) {
continue;
}
TypeModel typeModel = null;
if (builtin.equals("true") || builtin.equals("false")) {
typeModel = IntrinsicTypeModels.BOOL;
}
intermediateResults.put(builtin, new ConstReferenceCompletionItem(builtin, typeModel, false));
}
for (PackageModel packageModel : visiblePackages) {
Collection<? extends ConstModel> constants = packageModel.getConstants();
for (ConstModel model : constants) {
if (intermediateResults.containsKey(model.getName())) {
continue;
}
intermediateResults.put(model.getName(), new ConstReferenceCompletionItem(model, false));
}
Collection<? extends VarModel> vars = packageModel.getVars();
for (VarModel model : vars) {
if (intermediateResults.containsKey(model.getName())) {
continue;
}
intermediateResults.put(model.getName(), new VarReferenceCompletionItem(model, false));
}
Collection<? extends FunctionModel> functions = packageModel.getFunctions();
for (FunctionModel model : functions) {
if (model.getReceiverParameter() != null || intermediateResults.containsKey(model.getName())) {
continue;
}
intermediateResults.put(model.getName(), new FunctionReferenceCompletionItem(model));
}
}
LOGGER.log(Level.FINE, "TODO: proper block scope for vars");
ParseTree functionContext = getTopContext(parser, finalContext, new IntervalSet() {{ add(GoParser.RULE_functionDecl); add(GoParser.RULE_methodDecl); }});
if (functionContext != null) {
addVars(VarKind.RECEIVER, finalContext, localsAnalyzer.getReceiverParameters(functionContext), intermediateResults);
addVars(VarKind.PARAMETER, finalContext, localsAnalyzer.getParameters(functionContext), intermediateResults);
addVars(VarKind.RETURN, finalContext, localsAnalyzer.getReturnParameters(functionContext), intermediateResults);
addVars(VarKind.LOCAL, finalContext, localsAnalyzer.getLocals(functionContext), intermediateResults);
addVars(VarKind.LOCAL, finalContext, localsAnalyzer.getConstants(functionContext), intermediateResults);
}
}
} else if (finalContext instanceof GoParser.BaseTypeNameContext) {
// this is the name of a type in the current package
if (getFileModel() == null) {
continue;
}
PackageModel packageModel = getFileModel().getPackage();
Collection<? extends TypeModel> types = packageModel.getTypes();
for (TypeModel typeModel : types) {
if (intermediateResults.containsKey(typeModel.getName())) {
continue;
}
intermediateResults.put(typeModel.getName(), new TypeReferenceCompletionItem(typeModel));
}
} else if (finalContext instanceof GoParser.LabelContext) {
if (finalContext.getParent() instanceof GoParser.LabeledStmtContext) {
continue;
}
final List<Token> labels = new ArrayList<>();
ParseTreeListener listener = new GoParserBaseListener() {
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_labeledStmt, version=0)
public void enterLabeledStmt(LabeledStmtContext ctx) {
if (ctx.label() != null && ctx.label().IDENTIFIER() != null) {
labels.add(ctx.label().IDENTIFIER().getSymbol());
}
}
};
ParseTreeWalker.DEFAULT.walk(listener, entry.getKey());
for (Token token : labels) {
if (intermediateResults.containsKey(token.getText())) {
continue;
}
intermediateResults.put(token.getText(), new LabelReferenceCompletionItem(token.getText()));
}
LOGGER.log(Level.FINE, "TODO: Also include labels for the current function as obtained from the FunctionModel.");
} else if (finalContext instanceof GoParser.BuiltinCallContext) {
// this is easy - just add the names of built in methods
for (String builtin : SemanticHighlighter.PREDEFINED_FUNCTIONS) {
if (intermediateResults.containsKey(builtin)) {
continue;
}
intermediateResults.put(builtin, new FunctionReferenceCompletionItem(builtin));
}
} else if (finalContext instanceof GoParser.MethodNameContext) {
// This block only handles non-dotted identifiers, which means this can't be a methodExpr
// and is therefore always a declaration (never a reference).
continue;
} else if (finalContext instanceof GoParser.ReceiverContext) {
// this is a declaration not a reference
continue;
} else if (finalContext instanceof GoParser.FunctionDeclContext) {
// this is a declaration not a reference
continue;
} else if (finalContext instanceof GoParser.TypeSpecContext) {
// this is a declaration not a reference
continue;
} else if (finalContext instanceof GoParser.IdentifierListContext) {
// this is a declaration not a reference
continue;
} else if (finalContext instanceof GoParser.TypeSwitchGuardContext) {
// this is a declaration not a reference
continue;
}
}
}
final boolean sourceTestSource = getFileModel().getName().toLowerCase().endsWith("_test.go");
for (Iterator<Map.Entry<String, CompletionItem>> it = intermediateResults.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, CompletionItem> entry = it.next();
if (entry.getValue() instanceof GoCompletionItem) {
CodeElementModel codeElementModel = ((GoCompletionItem)entry.getValue()).getCodeElementModel();
if (codeElementModel == null || codeElementModel.getPackage() == null) {
continue;
}
final boolean inCurrentPackage = codeElementModel.getPackage().equals(getFileModel().getPackage());
if (!inCurrentPackage && !Character.isUpperCase(codeElementModel.getName().charAt(0))) {
// item is not visible to other packages
it.remove();
continue;
}
if (!(codeElementModel instanceof AbstractCodeElementModel)) {
continue;
}
FileModelImpl codeElementFile = ((AbstractCodeElementModel)codeElementModel).getFile();
if (codeElementFile == null) {
continue;
}
final boolean targetTestSource = codeElementFile.getName().toLowerCase().endsWith("_test.go");
if (targetTestSource && (!sourceTestSource || !inCurrentPackage)) {
// item is defined in a test source that isn't visible
it.remove();
continue;
}
}
}
results.addAll(intermediateResults.values());
}
} finally {
parser = null;
}
}
OffsetRegion applicableToSpan;
if (caretToken != null && caretToken.getOriginalToken() != null && caretToken.getOriginalToken().getChannel() == Token.DEFAULT_CHANNEL) {
applicableToSpan = OffsetRegion.fromBounds(caretToken.getStartIndex(), caretToken.getStopIndex() + 1);
} else {
SnapshotPositionRegion identifier = DocumentTextUtilities.getIdentifierBlock(new SnapshotPosition(snapshot, getCaretOffset()));
if (identifier != null) {
applicableToSpan = identifier.getRegion();
} else {
applicableToSpan = OffsetRegion.fromBounds(getCaretOffset(), getCaretOffset());
}
}
if (!isExtend() && applicableToSpan.contains(getCaretOffset())) {
applicableToSpan = OffsetRegion.fromBounds(applicableToSpan.getStart(), getCaretOffset());
}
if (!applicableToSpan.isEmpty()) {
// make sure this is a word
String applicableText = snapshot.subSequence(applicableToSpan.getStart(), applicableToSpan.getEnd()).toString();
if (!WORD_PATTERN.matcher(applicableText).matches()) {
applicableToSpan = OffsetRegion.fromBounds(getCaretOffset(), getCaretOffset());
}
}
applicableTo = snapshot.createTrackingRegion(applicableToSpan, TrackingPositionRegion.Bias.Inclusive);
if ((getQueryType() & CompletionProvider.TOOLTIP_QUERY_TYPE) == CompletionProvider.TOOLTIP_QUERY_TYPE) {
String enteredText = applicableTo.getText(snapshot);
List<CompletionItem> updated = new ArrayList<>();
for (CompletionItem item : results) {
if (item.getInsertPrefix().equals(enteredText)) {
updated.add(item);
}
}
results.clear();
results.addAll(updated);
if (updated.size() > 0) {
if (updated.get(0) instanceof GoCompletionItem) {
getToolTip().setTipText(((GoCompletionItem)updated.get(0)).getToolTipText());
} else {
getToolTip().setTipText(updated.get(0).getInsertPrefix().toString());
}
}
}
}
private void addVars(VarKind varKind, ParseTree finalContext, Collection<? extends Tuple3<? extends TerminalNode, ? extends ParserRuleContext, Integer>> vars, Map<String, ? super GoCompletionItem> intermediateResults) {
for (Tuple3<? extends TerminalNode, ? extends ParserRuleContext, Integer> varEntry : vars) {
String name = varEntry.getItem1().getText();
// make sure the item is visible in the final context
ParseTree scopeContext = ScopeContextVisitor.INSTANCE.visit(varEntry.getItem1());
if (!ParseTrees.isAncestorOf(scopeContext, finalContext)) {
continue;
}
int index = varEntry.getItem3();
if (index < 0) {
index = -index - 1;
}
Collection<? extends CodeElementModel> varTypes = targetAnalyzer.visit(varEntry.getItem2());
if (varTypes != null && index >= 0) {
ArrayList<CodeElementModel> unbundledTypes = new ArrayList<>();
for (CodeElementModel varType : varTypes) {
if (varType instanceof BundledReturnTypeModel) {
List<? extends CodeElementModel> returnValues = ((BundledReturnTypeModel)varType).getReturnValues();
if (returnValues.size() > index) {
unbundledTypes.add(((BundledReturnTypeModel)varType).getReturnValues().get(index));
}
} else if (index == 0) {
unbundledTypes.add(varType);
}
}
varTypes = unbundledTypes;
}
if (varTypes == null || varTypes.isEmpty()) {
Object existing = intermediateResults.get(name);
if (existing instanceof VarReferenceCompletionItem) {
VarModel existingModel = ((VarReferenceCompletionItem)existing).getCodeElementModel();
CodeElementPositionRegion seek = existingModel.getSeek();
if (seek != null && seek.getFileObject().equals(getFileModel().getFileObject())) {
if (seek.getOffsetRegion().getStart() == varEntry.getItem1().getSymbol().getStartIndex()) {
continue;
}
}
}
varTypes = Collections.singleton(new UnknownTypeModelImpl(getFileModel()));
}
if (varEntry.getItem3() < 0) {
ConstModelImpl constModel = new ConstModelImpl(name, getFileModel(), null, null, null, varEntry.getItem1(), varEntry.getItem2());
intermediateResults.put(name, new ConstReferenceCompletionItem(constModel, true));
} else {
for (CodeElementModel model : varTypes) {
TypeModelImpl typeModel;
if (model instanceof TypeModelImpl) {
typeModel = (TypeModelImpl)model;
} else if (model instanceof VarModelImpl) {
// this could be an implicit reference to a field or the return value of a function
VarModelImpl varModel = (VarModelImpl)model;
typeModel = varModel.getVarType();
} else if (model instanceof ConstModel) {
intermediateResults.put(name, new ConstReferenceCompletionItem((ConstModel)model, true));
continue;
} else {
LOGGER.log(Level.WARNING, "Unsupported code model: {0}", model.getClass());
continue;
}
VarModelImpl varModel = new VarModelImpl(name, varKind, typeModel, getFileModel(), varEntry.getItem1(), varEntry.getItem2());
intermediateResults.put(name, new VarReferenceCompletionItem(varModel, true));
break;
}
}
}
}
private FileModelImpl getFileModel() {
if (fileModel == null && !fileModelDataFailed) {
Future<ParserData<FileModel>> futureFileModelData = taskManager.getData(snapshot, GoParserDataDefinitions.FILE_MODEL, EnumSet.of(ParserDataOptions.ALLOW_STALE, ParserDataOptions.SYNCHRONOUS));
try {
fileModel = futureFileModelData != null ? (FileModelImpl)futureFileModelData.get().getData() : null;
fileModelDataFailed = fileModel != null;
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while getting the file model.", ex);
fileModelDataFailed = true;
}
}
return fileModel;
}
private final ObjectDecorator<ParseTree> annotations = new ObjectDecorator<>();
private class LocalsAnalyzer {
// private final List<Token> labels = new ArrayList<Token>();
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getLocals(@NonNull ParseTree context) {
Parameters.notNull("context", context);
return getLocals(context, ATTR_LOCALS);
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getConstants(@NonNull ParseTree context) {
Parameters.notNull("context", context);
return getLocals(context, ATTR_CONSTANTS);
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getReceiverParameters(@NonNull ParseTree context) {
Parameters.notNull("context", context);
return getLocals(context, ATTR_RECEIVER_PARAMETER);
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getParameters(@NonNull ParseTree context) {
Parameters.notNull("context", context);
return getLocals(context, ATTR_PARAMETER);
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getReturnParameters(@NonNull ParseTree context) {
Parameters.notNull("context", context);
return getLocals(context, ATTR_RETURN_PARAMETER);
}
private Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getLocals(ParseTree context, ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> property) {
Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> result = getLocalsProperty(context, property);
if (result != null) {
return result;
}
Listener listener = new Listener();
ParseTreeWalker.DEFAULT.walk(listener, context);
setLocalsProperty(context, ATTR_LOCALS, listener.getLocals());
setLocalsProperty(context, ATTR_CONSTANTS, listener.getConstants());
setLocalsProperty(context, ATTR_PARAMETER, listener.getParameters());
setLocalsProperty(context, ATTR_RECEIVER_PARAMETER, listener.getReceiverParameters());
setLocalsProperty(context, ATTR_RETURN_PARAMETER, listener.getReturnParameters());
result = getLocalsProperty(context, property);
if (result == null) {
LOGGER.log(Level.FINE, "TODO: resolve locals");
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
// public List<Token> getLabels() {
// return labels;
// }
private Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getLocalsProperty(ParseTree context, ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> property) {
Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> result = annotations.getProperty(context, property);
if (result != null) {
return result;
}
return null;
}
private void setLocalsProperty(ParseTree context, ObjectProperty<Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>>> property, @NonNull Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> locals) {
Parameters.notNull("locals", locals);
annotations.putProperty(context, property, locals);
}
private class Listener extends GoParserBaseListener {
private final Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> locals = new ArrayList<>();
private final Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> receiverParameters = new ArrayList<>();
private final Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> parameters = new ArrayList<>();
private final Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> returnParameters = new ArrayList<>();
private final Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> constants = new ArrayList<>();
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getLocals() {
return locals;
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getConstants() {
return constants;
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getReceiverParameters() {
return receiverParameters;
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getParameters() {
return parameters;
}
public Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> getReturnParameters() {
return returnParameters;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_varSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expressionList, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
})
public void enterVarSpec(VarSpecContext ctx) {
addVars(locals, ctx.identifierList(), ctx.type(), ctx.expressionList());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_shortVarDecl, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expressionList, version=1),
})
public void enterShortVarDecl(ShortVarDeclContext ctx) {
addVars(locals, ctx.identifierList(), null, ctx.expressionList());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_rangeClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
})
public void enterRangeClause(RangeClauseContext ctx) {
if (ctx.defeq != null) {
if (ctx.e1 != null && ctx.e1.start != null) {
locals.add(Tuple.create(getStartNode(ctx.e1), (ParserRuleContext)ctx, 0));
}
if (ctx.e2 != null && ctx.e2.start != null) {
locals.add(Tuple.create(getStartNode(ctx.e2), (ParserRuleContext)ctx, 1));
}
}
}
private TerminalNode getStartNode(ParseTree context) {
if (context instanceof TerminalNode) {
return (TerminalNode)context;
}
for (int i = 0; i < context.getChildCount(); i++) {
TerminalNode node = getStartNode(context.getChild(i));
if (node != null) {
return node;
}
}
return null;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchGuard, version=0)
public void enterTypeSwitchGuard(TypeSwitchGuardContext ctx) {
if (ctx.IDENTIFIER() != null) {
locals.add(Tuple.create(ctx.IDENTIFIER(), (ParserRuleContext)ctx, 0));
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_recvStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
})
public void enterRecvStmt(RecvStmtContext ctx) {
if (ctx.defeq != null) {
if (ctx.e1 != null && ctx.e1.start != null) {
locals.add(Tuple.create(getStartNode(ctx.e1), (ParserRuleContext)ctx.recvExpr(), 0));
}
if (ctx.e2 != null && ctx.e2.start != null) {
locals.add(Tuple.create(getStartNode(ctx.e2), (ParserRuleContext)ctx.recvExpr(), 1));
}
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_receiver, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseTypeName, version=0),
})
public void enterReceiver(ReceiverContext ctx) {
if (ctx.IDENTIFIER() != null) {
receiverParameters.add(Tuple.create(ctx.IDENTIFIER(), (ParserRuleContext)ctx.baseTypeName(), 0));
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameters, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_result, version=0),
})
public void enterParameterDecl(ParameterDeclContext ctx) {
if (ctx.identifierList() != null) {
GoParser.ParametersContext parametersContext = (GoParser.ParametersContext)getTopContext(parser, ctx, IntervalSet.of(GoParser.RULE_parameters));
Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> vars = parametersContext.parent instanceof GoParser.ResultContext ? returnParameters : parameters;
addVars(vars, ctx.identifierList(), ctx.type(), null);
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_constSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expressionList, version=1),
})
public void enterConstSpec(ConstSpecContext ctx) {
addVars(constants, ctx.identifierList(), ctx.type(), ctx.expressionList());
}
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expressionList, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_constSpec, version=0),
})
private void addVars(@NonNull Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> vars,
@NullAllowed GoParser.IdentifierListContext idList,
@NullAllowed GoParser.TypeContext explicitType,
@NullAllowed GoParser.ExpressionListContext exprList) {
if (idList == null || idList.IDENTIFIER() == null) {
return;
}
List<? extends GoParser.ExpressionContext> expressions = exprList != null ? exprList.expression() : null;
for (int i = 0; i < idList.IDENTIFIER().size(); i++) {
TerminalNode name = idList.IDENTIFIER(i);
ParserRuleContext type = explicitType;
int index = type != null ? 0 : i;
if (type == null && expressions != null) {
if (i < expressions.size()) {
type = expressions.get(i);
index = 0;
} else if (expressions.size() == 1) {
type = expressions.get(0);
}
}
if (type == null) {
LOGGER.log(Level.FINE, "Couldn't resolve expression type.");
continue;
}
if (idList.getParent() instanceof ConstSpecContext) {
index = -index - 1;
}
vars.add(Tuple.create(name, type, index));
}
}
}
}
private class TargetAnalyzer extends GoParserBaseVisitor<Collection<? extends CodeElementModel>> {
@Override
public Collection<? extends CodeElementModel> visitChildren(RuleNode node) {
LOGGER.log(Level.WARNING, "Context {0} is not supported by this visitor.", node.getClass());
return Collections.emptyList();
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageClause, version=0),
})
public Collection<? extends CodeElementModel> visitPackageName(PackageNameContext ctx) {
FileModel fileModel = getFileModel();
if (ctx.IDENTIFIER() == null || fileModel == null) {
return Collections.emptyList();
}
if (ctx.getParent() instanceof GoParser.PackageClauseContext) {
Collection<? extends CodeElementModel> resolved = Collections.singletonList(fileModel.getPackage());
setTargetProperty(ctx, resolved);
return resolved;
} else {
// look up the package by import
CodeModelCache cache = CodeModelCacheImpl.getInstance();
Collection<CodeElementModel> resolved = new ArrayList<>();
Collection<? extends ImportDeclarationModel> imports = fileModel.getImportDeclarations();
for (ImportDeclarationModel model : imports) {
if (model.getName().equals(ctx.start.getText())) {
resolved.addAll(cache.getPackages(fileModel.getPackage().getProject(), model.getPath()));
}
}
setTargetProperty(ctx, resolved);
return resolved;
}
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitUnaryExpr(UnaryExprContext ctx) {
if (ctx.op == null) {
return Collections.emptyList();
}
switch (ctx.op.getType()) {
case GoParser.Star:
// dereference value
Collection<? extends CodeElementModel> pointerTypes = visit(ctx.expression());
Collection<CodeElementModel> result = new ArrayList<>();
for (CodeElementModel model : pointerTypes) {
for (TypeModelImpl typeModel : resolveType(model, true, false)) {
if (!(typeModel instanceof TypePointerModel)) {
LOGGER.log(Level.WARNING, "Cannot dereference expression of type {0}.", typeModel);
continue;
}
result.add(((TypePointerModel)typeModel).getElementType());
}
}
setTargetProperty(ctx, result);
return result;
case GoParser.Amp:
// dereference value
Collection<? extends CodeElementModel> types = visit(ctx.expression());
result = new ArrayList<>();
for (CodeElementModel model : types) {
TypeModel typeModel;
if (model instanceof TypeModel) {
typeModel = (TypeModel)model;
} else if (model instanceof VarModel) {
typeModel = ((VarModel)model).getVarType();
} else {
LOGGER.log(Level.WARNING, "Cannot deference expression model of {0}.", model.getClass());
continue;
}
if (!(typeModel instanceof TypeModelImpl)) {
continue;
}
result.add(new TypePointerModelImpl((TypeModelImpl)typeModel));
}
setTargetProperty(ctx, result);
return result;
default:
LOGGER.log(Level.WARNING, "Unary operator {0} is not supported by this visitor.", ctx.op.getText());
return Collections.emptyList();
}
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitMultExpr(MultExprContext ctx) {
if (ctx.op == null) {
return Collections.emptyList();
}
switch (ctx.op.getType()) {
case GoLexer.LeftShift:
case GoLexer.RightShift:
return visit(ctx.expression(0));
case GoLexer.Star:
case GoLexer.Slash:
case GoLexer.Percent:
case GoLexer.Amp:
case GoLexer.AmpCaret:
// this may not be absolutely precise, but for now it's close enough to make the UI work
return visit(ctx.expression(0));
default:
LOGGER.log(Level.WARNING, "Multiply operator {0} is not supported by this visitor.", ctx.op.getText());
return Collections.emptyList();
}
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitAddExpr(AddExprContext ctx) {
if (ctx.op == null) {
return Collections.emptyList();
}
switch (ctx.op.getType()) {
case GoLexer.Plus:
case GoLexer.Minus:
case GoLexer.Pipe:
case GoLexer.Caret:
// this may not be absolutely precise, but for now it's close enough to make the UI work
return visit(ctx.expression(0));
default:
LOGGER.log(Level.WARNING, "Add operator {0} is not supported by this visitor.", ctx.op.getText());
return Collections.emptyList();
}
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitIndexExpr(IndexExprContext ctx) {
List<? extends ExpressionContext> expressions = ctx.expression();
if (expressions.isEmpty()) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> target = visit(expressions.get(0));
List<CodeElementModel> result = new ArrayList<>();
for (CodeElementModel model : target) {
for (TypeModelImpl type : resolveType(model, true, true)) {
if (type instanceof TypeArrayModel || type instanceof TypeSliceModel) {
result.add(((TypeWrapperModel)type).getElementType());
} else if (type instanceof TypeMapModel) {
result.add(new BundledReturnTypeModel(Arrays.asList((TypeModelImpl)((TypeMapModel)type).getValueType(), (TypeModelImpl)IntrinsicTypeModels.BOOL)));
} else if (IntrinsicTypeModels.STRING.equals(type)) {
result.add(IntrinsicTypeModels.BYTE);
} else {
LOGGER.log(Level.WARNING, "Cannot resolve index expression for source model {0}.", type.getClass());
}
}
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitSliceExpr(SliceExprContext ctx) {
List<? extends ExpressionContext> expressions = ctx.expression();
if (expressions.isEmpty()) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> target = visit(expressions.get(0));
List<CodeElementModel> result = new ArrayList<>();
for (CodeElementModel model : target) {
for (TypeModelImpl type : resolveType(model, true, true)) {
if (type instanceof TypeArrayModel) {
result.add(new TypeSliceModelImpl(((TypeArrayModelImpl)type).getElementType()));
} else if (type instanceof TypeSliceModel || IntrinsicTypeModels.STRING.equals(type)) {
result.add(type);
} else {
LOGGER.log(Level.WARNING, "Unsupported type in slice expression: {0}", model.getClass());
}
}
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_operand, version=0),
})
public Collection<? extends CodeElementModel> visitOperandExpr(OperandExprContext ctx) {
OperandContext operandContext = ctx.operand();
if (operandContext != null) {
Collection<? extends CodeElementModel> resolved = visit(operandContext);
setTargetProperty(ctx, resolved);
return resolved;
} else {
LOGGER.log(Level.WARNING, "TODO: handle other expressions.");
return Collections.emptyList();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_builtinCall, version=0),
})
public Collection<? extends CodeElementModel> visitBuiltinCallExpr(BuiltinCallExprContext ctx) {
BuiltinCallContext builtinCallContext = ctx.builtinCall();
if (builtinCallContext == null) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> result = visit(builtinCallContext);
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_builtinCall, version=0, dependents=Dependents.PARENTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_builtinArgs, version=2, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitBuiltinCall(BuiltinCallContext ctx) {
TerminalNode methodName = ctx.IDENTIFIER();
if (methodName == null) {
return Collections.emptyList();
}
String name = methodName.getText();
if (name == null || name.isEmpty()) {
return Collections.emptyList();
}
BuiltinArgsContext args = ctx.builtinArgs();
TypeContext type = args != null ? args.type() : null;
switch (name) {
case "append":
case "make":
if (type == null) {
return Collections.emptyList();
}
return visit(type);
case "cap":
case "copy":
case "len":
return Collections.singletonList(IntrinsicTypeModels.INT);
case "close":
case "delete":
case "panic":
case "print":
case "println":
// no return value
return Collections.emptyList();
case "complex":
LOGGER.log(Level.FINE, "TODO: separate complex64 and complex128 types.");
return Collections.singletonList(IntrinsicTypeModels.COMPLEX128);
case "imag":
case "real":
LOGGER.log(Level.FINE, "TODO: separate float32 and float64 types.");
return Collections.singletonList(IntrinsicTypeModels.FLOAT64);
case "new":
{
if (type == null) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> result = visit(type);
if (result.isEmpty()) {
return result;
}
List<CodeElementModel> pointers = new ArrayList<>();
for (CodeElementModel model : result) {
if (model instanceof TypeModelImpl) {
pointers.add(new TypePointerModelImpl((TypeModelImpl)model));
}
}
setTargetProperty(ctx, pointers);
return pointers;
}
case "recover":
{
TypeInterfaceModelImpl result = new TypeInterfaceModelImpl("_", fileModel, ctx);
result.freeze();
return Collections.singletonList(result);
}
default:
assert !SemanticHighlighter.PREDEFINED_FUNCTIONS.contains(name);
Collection<? extends CodeElementModel> methodResults = analyzeUnqualifiedIdentifier(ctx.IDENTIFIER());
List<CodeElementModel> results = new ArrayList<>();
for (CodeElementModel model : methodResults) {
if (model instanceof TypeModel) {
results.add(model);
} else if (model instanceof FunctionModel) {
// some calls look like conversions
Collection<? extends AbstractCodeElementModel> returnValues;
if (model instanceof FunctionModelImpl) {
returnValues = ((FunctionModelImpl)model).getReturnValues();
} else if (model instanceof TypeFunctionModelImpl) {
returnValues = ((TypeFunctionModelImpl)model).getReturnValues();
} else {
LOGGER.log(Level.WARNING, "Unsupported {0} implementation: {1}.", new Object[] { FunctionModel.class.getSimpleName(), model.getClass().getName() } );
continue;
}
if (returnValues.size() > 1) {
returnValues = Collections.singletonList(new BundledReturnTypeModel(returnValues));
}
results.addAll(returnValues);
}
}
setTargetProperty(ctx, results);
return results;
}
}
/**
* Hack! Copied from {@link #visitQualifiedIdentifier}.
*/
@NonNull
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionDecl, version=3, dependents=Dependents.DESCENDANTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=3, dependents=Dependents.DESCENDANTS),
})
private Collection<? extends CodeElementModel> analyzeUnqualifiedIdentifier(@NullAllowed TerminalNode nameNode) {
if (nameNode == null) {
return Collections.emptyList();
}
// HACK! copied from visitQualifiedIdentifier
Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> vars = Collections.emptyList();
List<CodeElementModel> contextModels = new ArrayList<>();
List<ImportDeclarationModel> possibleImports = new ArrayList<>();
contextModels.add(getFileModel().getPackage());
for (ImportDeclarationModel importDeclarationModel : getFileModel().getImportDeclarations()) {
if (importDeclarationModel.isMergeWithLocal()) {
possibleImports.add(importDeclarationModel);
}
}
ParseTree functionContext = getTopContext(parser, nameNode.getParent().getRuleContext(), new IntervalSet() {{ add(GoParser.RULE_functionDecl); add(GoParser.RULE_methodDecl); }});
if (functionContext != null) {
vars = new ArrayList<>();
vars.addAll(localsAnalyzer.getReceiverParameters(functionContext));
vars.addAll(localsAnalyzer.getParameters(functionContext));
vars.addAll(localsAnalyzer.getReturnParameters(functionContext));
vars.addAll(localsAnalyzer.getConstants(functionContext));
vars.addAll(localsAnalyzer.getLocals(functionContext));
}
PackageModel builtinPackage = CodeModelCacheImpl.getInstance().getUniquePackage(getFileModel().getPackage().getProject(), "builtin");
if (builtinPackage != null) {
contextModels.add(builtinPackage);
}
for (ImportDeclarationModel importDeclarationModel : possibleImports) {
Collection<? extends PackageModel> resolved = CodeModelCacheImpl.getInstance().resolvePackages(importDeclarationModel);
if (resolved != null) {
contextModels.addAll(resolved);
}
}
Set<CodeElementModel> members = new HashSet<>();
String name = nameNode.getSymbol().getText();
for (Tuple3<TerminalNode, ParserRuleContext, Integer> entry : vars) {
if (ParseTrees.isAncestorOf(entry.getItem2(), nameNode)) {
// prevent stack overflow from redeclaration of a variable
continue;
}
if (!name.equals(entry.getItem1().getText())) {
continue;
}
Collection<? extends CodeElementModel> varTypes = visit(entry.getItem2());
ArrayList<CodeElementModel> unbundledTypes = new ArrayList<>();
for (CodeElementModel varType : varTypes) {
if (varType instanceof BundledReturnTypeModel) {
List<? extends CodeElementModel> returnValues = ((BundledReturnTypeModel)varType).getReturnValues();
if (returnValues.size() > entry.getItem3()) {
unbundledTypes.add(((BundledReturnTypeModel)varType).getReturnValues().get(entry.getItem3()));
}
} else if (entry.getItem3() == 0) {
unbundledTypes.add(varType);
}
}
varTypes = unbundledTypes;
if (varTypes.isEmpty()) {
varTypes = Collections.singleton(new UnknownTypeModelImpl(getFileModel()));
}
for (CodeElementModel unresolvedVarType : varTypes) {
for (TypeModelImpl varType : resolveType(unresolvedVarType, false, false)) {
// TODO: use proper var kind
VarModelImpl varModel = new VarModelImpl(name, VarKind.LOCAL, varType, getFileModel(), entry.getItem1(), entry.getItem2());
members.add(varModel);
}
}
}
if (members.isEmpty()) {
for (CodeElementModel model : contextModels) {
members.addAll(model.getMembers(name));
}
}
return members;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitSelectorExpr(SelectorExprContext ctx) {
if (ctx.IDENTIFIER() == null) {
setTargetProperty(ctx, Collections.<CodeElementModel>emptyList());
return Collections.emptyList();
}
String name = ctx.IDENTIFIER().getSymbol().getText();
if (name == null || name.isEmpty()) {
setTargetProperty(ctx, Collections.<CodeElementModel>emptyList());
return Collections.emptyList();
}
Collection<? extends CodeElementModel> targets = visit(ctx.expression());
List<CodeElementModel> members = new ArrayList<>();
for (CodeElementModel target : targets) {
members.addAll(SemanticAnalyzer.getSelectableMembers(target, name));
}
setTargetProperty(ctx, members);
return members;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=2, dependents=Dependents.PARENTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeName, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeLiteral, version=0),
})
public Collection<? extends CodeElementModel> visitType(TypeContext ctx) {
Collection<? extends CodeElementModel> result;
if (ctx.typeName() != null) {
result = visit(ctx.typeName());
} else if (ctx.typeLiteral() != null) {
result = visit(ctx.typeLiteral());
} else if (ctx.type() != null) {
result = visit(ctx.type());
} else {
LOGGER.log(Level.WARNING, "Unknown type syntax.");
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeLiteral, version=0)
public Collection<? extends CodeElementModel> visitTypeLiteral(TypeLiteralContext ctx) {
assert ctx.getChildCount() <= 1 : "Unknown typeLiteral syntax.";
Collection<? extends CodeElementModel> result = visit(ctx.getChild(0));
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_arrayType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitArrayType(ArrayTypeContext ctx) {
List<CodeElementModel> result = new ArrayList<>();
if (ctx.elementType() != null) {
result.addAll(visit(ctx.elementType()));
}
for (int i = result.size() - 1; i >= 0; i--) {
if (!(result.get(i) instanceof TypeModelImpl)) {
result.remove(i);
continue;
}
result.set(i, new TypeArrayModelImpl((TypeModelImpl)result.get(i)));
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_keyType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitKeyType(KeyTypeContext ctx) {
if (ctx.type() == null) {
return Collections.emptyList();
}
return visit(ctx.type());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitElementType(ElementTypeContext ctx) {
Collection<? extends CodeElementModel> result;
if (ctx.type() != null) {
result = visit(ctx.type());
} else {
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_structType, version=0)
public Collection<? extends CodeElementModel> visitStructType(StructTypeContext ctx) {
LOGGER.log(Level.WARNING, "Target resolution for context {0} is not implemented.", ctx.getClass());
return Collections.emptyList();
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_pointerType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseType, version=0),
})
public Collection<? extends CodeElementModel> visitPointerType(PointerTypeContext ctx) {
List<CodeElementModel> result = new ArrayList<>();
if (ctx.baseType() != null) {
result.addAll(visit(ctx.baseType()));
}
for (int i = result.size() - 1; i >= 0; i--) {
if (!(result.get(i) instanceof TypeModelImpl)) {
result.remove(i);
continue;
}
result.set(i, new TypePointerModelImpl((TypeModelImpl)result.get(i)));
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseType, version=0)
public Collection<? extends CodeElementModel> visitBaseType(BaseTypeContext ctx) {
if (ctx.getChildCount() != 1) {
LOGGER.log(Level.WARNING, "Unknown baseType syntax.");
return Collections.emptyList();
}
Collection<? extends CodeElementModel> result = visit(ctx.type());
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_signature, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameters, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_result, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitFunctionType(FunctionTypeContext ctx) {
TypeFunctionModelImpl functionType = new TypeFunctionModelImpl("func", fileModel, ctx);
List<ParameterModelImpl> parameters = functionType.getParameters();
List<ParameterModelImpl> returnValues = functionType.getReturnValues();
SignatureContext signatureContext = ctx.signature();
if (signatureContext == null) {
return Collections.emptyList();
}
ParametersContext parametersContext = signatureContext.parameters();
handleParameters(parametersContext, VarKind.PARAMETER, parameters);
ResultContext resultContext = signatureContext.result();
if (resultContext != null) {
parametersContext = resultContext.parameters();
if (parametersContext != null) {
handleParameters(parametersContext, VarKind.RETURN, returnValues);
} else {
TypeContext typeContext = resultContext.type();
if (typeContext != null) {
Collection<? extends CodeElementModel> resolved = visit(typeContext);
TypeModelImpl returnType;
if (resolved.size() == 1) {
returnType = (TypeModelImpl)resolved.iterator().next();
} else {
returnType = new UnknownTypeModelImpl(fileModel);
}
returnValues.add(new ParameterModelImpl("_", VarKind.RETURN, returnType, fileModel, ParseTrees.getStartNode(typeContext), typeContext));
}
}
}
functionType.freeze();
return Collections.singletonList(functionType);
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_rangeClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
})
public Collection<? extends CodeElementModel> visitRangeClause(RangeClauseContext ctx) {
if (ctx.e == null) {
return Collections.emptyList();
}
// this will be a bundled return type
Collection<? extends CodeElementModel> expressionModel = visit(ctx.e);
List<CodeElementModel> result = new ArrayList<>();
for (CodeElementModel model : expressionModel) {
for (TypeModelImpl type : resolveType(model, true, true)) {
if (type instanceof TypeArrayModelImpl || type instanceof TypeSliceModelImpl) {
TypeWrapperModelImpl wrapperModel = (TypeWrapperModelImpl)type;
result.add(new BundledReturnTypeModel(Arrays.asList((TypeModelImpl)IntrinsicTypeModels.INT, wrapperModel.getElementType())));
} else if (type instanceof TypeMapModelImpl) {
TypeMapModelImpl mapModel = (TypeMapModelImpl)type;
result.add(new BundledReturnTypeModel(Arrays.asList(mapModel.getKeyType(), mapModel.getValueType())));
} else if (IntrinsicTypeModels.STRING.equals(type)) {
result.add(new BundledReturnTypeModel(Arrays.asList((TypeModelImpl)IntrinsicTypeModels.INT, (TypeModelImpl)IntrinsicTypeModels.RUNE)));
} else if (type instanceof TypeChannelModelImpl) {
result.add(((TypeChannelModelImpl)type).getElementType());
} else {
LOGGER.log(Level.WARNING, "Unsupported type model {0} in range clause.", type.getClass());
}
}
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchGuard, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeCaseClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchCase, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitTypeSwitchGuard(TypeSwitchGuardContext ctx) {
if (ctx.expression() == null) {
return Collections.emptyList();
}
TypeSwitchStmtContext typeSwitchStmtContext = (TypeSwitchStmtContext)ctx.getParent();
List<? extends TypeCaseClauseContext> typeCaseClauseContexts = typeSwitchStmtContext.typeCaseClause();
if (typeCaseClauseContexts.isEmpty()) {
return visit(ctx.expression());
}
TypeCaseClauseContext lastContext = typeCaseClauseContexts.get(typeCaseClauseContexts.size() - 1);
TypeSwitchCaseContext typeSwitchCaseContext = lastContext.typeSwitchCase();
if (typeSwitchCaseContext == null) {
return visit(ctx.expression());
}
TypeListContext typeListContext = typeSwitchCaseContext.typeList();
if (typeListContext == null || typeListContext.type().size() != 1) {
return visit(ctx.expression());
}
return visit(typeListContext.type(0));
}
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameters, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
})
private void handleParameters(ParametersContext parametersContext, VarKind kind, List<ParameterModelImpl> parameters) {
ParameterListContext parameterListContext = parametersContext.parameterList();
if (parameterListContext == null) {
return;
}
List<? extends ParameterDeclContext> parameterDeclContexts = parameterListContext.parameterDecl();
for (ParameterDeclContext parameterDeclContext : parameterDeclContexts) {
TypeContext typeContext = parameterDeclContext.type();
Collection<? extends CodeElementModel> resolved = typeContext != null ? visit(typeContext) : Collections.<CodeElementModel>emptyList();
TypeModelImpl returnType;
if (resolved.size() == 1) {
returnType = (TypeModelImpl)resolved.iterator().next();
} else {
returnType = new UnknownTypeModelImpl(fileModel);
}
IdentifierListContext identifierListContext = parameterDeclContext.identifierList();
if (identifierListContext == null) {
continue;
}
for (TerminalNode identifier : identifierListContext.IDENTIFIER()) {
String name = identifier.getText();
if (name == null || name.isEmpty()) {
name = "_";
}
ParameterModelImpl parameter = new ParameterModelImpl(name, kind, returnType, fileModel, identifier, parameterDeclContext);
parameters.add(parameter);
}
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_interfaceType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodSpec, version=0),
})
public Collection<? extends CodeElementModel> visitInterfaceType(InterfaceTypeContext ctx) {
if (!ctx.methodSpec().isEmpty()) {
LOGGER.log(Level.WARNING, "Target resolution for non-empty anonymous interfaces is not implemented.");
}
return Collections.singletonList(new TypeInterfaceModelImpl("_", fileModel, ctx));
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_sliceType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitSliceType(SliceTypeContext ctx) {
List<CodeElementModel> result = new ArrayList<>();
if (ctx.elementType() != null) {
result.addAll(visit(ctx.elementType()));
}
for (int i = result.size() - 1; i >= 0; i--) {
if (!(result.get(i) instanceof TypeModelImpl)) {
result.remove(i);
continue;
}
result.set(i, new TypeSliceModelImpl((TypeModelImpl)result.get(i)));
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_mapType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_keyType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitMapType(MapTypeContext ctx) {
Collection<? extends CodeElementModel> keyTypes = null;
Collection<? extends CodeElementModel> valueTypes = null;
List<CodeElementModel> result = new ArrayList<>();
if (ctx.keyType() != null) {
keyTypes = visit(ctx.keyType());
}
if (ctx.elementType() != null) {
valueTypes = visit(ctx.elementType());
}
if (keyTypes == null || keyTypes.isEmpty()) {
keyTypes = Collections.singletonList(new UnknownTypeModelImpl(fileModel));
}
if (valueTypes == null || valueTypes.isEmpty()) {
valueTypes = Collections.singletonList(new UnknownTypeModelImpl(fileModel));
}
for (CodeElementModel keyTypeModel : keyTypes) {
for (TypeModelImpl keyType : resolveType(keyTypeModel, false, false)) {
for (CodeElementModel valueTypeModel : valueTypes) {
for (TypeModelImpl valueType : resolveType(valueTypeModel, false, false)) {
result.add(new TypeMapModelImpl(keyType, valueType));
}
}
}
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_channelType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitChannelType(ChannelTypeContext ctx) {
List<CodeElementModel> result = new ArrayList<>();
if (ctx.elementType() != null) {
result.addAll(visit(ctx.elementType()));
}
ChannelKind channelKind = ChannelKind.SendReceive;
if (ctx.send != null) {
channelKind = ChannelKind.Send;
} else if (ctx.recv != null) {
channelKind = ChannelKind.Receive;
}
for (int i = result.size() - 1; i >= 0; i--) {
if (!(result.get(i) instanceof TypeModelImpl)) {
result.remove(i);
continue;
}
result.set(i, new TypeChannelModelImpl((TypeModelImpl)result.get(i), channelKind));
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_literalType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitLiteralType(LiteralTypeContext ctx) {
if (ctx.elementType() != null) {
LOGGER.log(Level.WARNING, "TODO: resolve implicit array creation.");
return Collections.emptyList();
}
assert ctx.getChildCount() <= 1 : "Unknown literalType syntax.";
Collection<? extends CodeElementModel> result = visit(ctx.getChild(0));
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeName, version=3, dependents=Dependents.PARENTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitTypeName(TypeNameContext ctx) {
QualifiedIdentifierContext qualifiedIdentifierContext = ctx.qualifiedIdentifier();
if (qualifiedIdentifierContext == null) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> result = visit(ctx.qualifiedIdentifier());
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseTypeName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_receiver, version=0),
})
public Collection<? extends CodeElementModel> visitBaseTypeName(BaseTypeNameContext ctx) {
// must be a type in the current package
PackageModel currentPackage = getFileModel().getPackage();
Collection<? extends TypeModel> types = currentPackage.getTypes(ctx.IDENTIFIER().getSymbol().getText());
List<CodeElementModel> result = new ArrayList<>();
ReceiverContext receiverContext = (ReceiverContext)ctx.parent;
boolean isptr = receiverContext.ptr != null;
if (isptr) {
for (TypeModel model : types) {
assert model instanceof TypeModelImpl;
if (!(model instanceof TypeModelImpl)) {
continue;
}
TypePointerModel ptr = new TypePointerModelImpl((TypeModelImpl)model);
result.add(ptr);
}
} else {
result.addAll(types);
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_operand, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_literal, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodExpr, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
})
public Collection<? extends CodeElementModel> visitOperand(OperandContext ctx) {
Collection<? extends CodeElementModel> result;
if (ctx.literal() != null) {
result = visit(ctx.literal());
} else if (ctx.qualifiedIdentifier() != null) {
result = visit(ctx.qualifiedIdentifier());
} else if (ctx.methodExpr() != null) {
result = visit(ctx.methodExpr());
} else if (ctx.expression() != null) {
result = visit(ctx.expression());
} else {
LOGGER.log(Level.WARNING, "TODO: unknown typeName syntax.");
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_conversion, version=0),
})
public Collection<? extends CodeElementModel> visitConversionOrCallExpr(ConversionOrCallExprContext ctx) {
if (ctx.conversion() == null) {
return Collections.emptyList();
}
return visit(ctx.conversion());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_conversion, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitConversion(ConversionContext ctx) {
if (ctx.type() == null) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> methodResults = visit(ctx.type());
List<CodeElementModel> results = new ArrayList<>();
for (CodeElementModel model : methodResults) {
if (model instanceof TypeModel) {
results.add(model);
} else if (model instanceof FunctionModel) {
// some calls look like conversions
Collection<? extends AbstractCodeElementModel> returnValues;
if (model instanceof FunctionModelImpl) {
returnValues = ((FunctionModelImpl)model).getReturnValues();
} else if (model instanceof TypeFunctionModelImpl) {
returnValues = ((TypeFunctionModelImpl)model).getReturnValues();
} else {
LOGGER.log(Level.WARNING, "Unsupported {0} implementation: {1}.", new Object[] { FunctionModel.class.getSimpleName(), model.getClass().getSimpleName() } );
continue;
}
if (returnValues.size() > 1) {
returnValues = Collections.singletonList(new BundledReturnTypeModel(returnValues));
}
results.addAll(returnValues);
}
}
setTargetProperty(ctx, results);
return results;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitAndExpr(AndExprContext ctx) {
return Collections.singletonList(IntrinsicTypeModels.BOOL);
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitOrExpr(OrExprContext ctx) {
return Collections.singletonList(IntrinsicTypeModels.BOOL);
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public Collection<? extends CodeElementModel> visitCallExpr(CallExprContext ctx) {
if (ctx.expression() == null) {
return Collections.emptyList();
}
Collection<? extends CodeElementModel> methodResults = visit(ctx.expression());
List<CodeElementModel> results = new ArrayList<>();
for (CodeElementModel model : methodResults) {
Collection<? extends CodeElementModel> resolvedModels;
if (model instanceof VarModel) {
resolvedModels = resolveType(model, true, false);
} else {
resolvedModels = Collections.singletonList(model);
}
for (CodeElementModel resolvedModel : resolvedModels) {
if (resolvedModel instanceof FunctionModel) {
Collection<? extends AbstractCodeElementModel> returnValues;
if (resolvedModel instanceof FunctionModelImpl) {
returnValues = ((FunctionModelImpl)resolvedModel).getReturnValues();
} else if (resolvedModel instanceof TypeFunctionModelImpl) {
returnValues = ((TypeFunctionModelImpl)resolvedModel).getReturnValues();
} else {
LOGGER.log(Level.WARNING, "Unsupported {0} implementation: {1}.", new Object[] { FunctionModel.class.getSimpleName(), resolvedModel.getClass().getSimpleName() } );
continue;
}
if (returnValues.size() > 1) {
returnValues = Collections.singletonList(new BundledReturnTypeModel(returnValues));
}
results.addAll(returnValues);
} else if (model instanceof TypeModel) {
// conversion operations look like calls
results.add(resolvedModel);
} else {
LOGGER.log(Level.WARNING, "Unsupported target for call expression: {0}", resolvedModel.getClass());
}
}
}
setTargetProperty(ctx, results);
return results;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0, dependents=Dependents.PARENTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionDecl, version=3, dependents=Dependents.DESCENDANTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=3, dependents=Dependents.DESCENDANTS),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageName, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitQualifiedIdentifier(QualifiedIdentifierContext ctx) {
// first check for the "semi-special" literals
if (ctx.packageName() == null && ctx.IDENTIFIER() != null) {
TerminalNode identifier = ctx.IDENTIFIER();
switch (identifier.getText()) {
case "true":
case "false":
return Collections.singletonList(IntrinsicTypeModels.BOOL);
case "iota":
return Collections.singletonList(IntrinsicTypeModels.INT);
}
TypeModel intrinsic = IntrinsicTypeModels.getIntrinsicType(identifier.getText());
if (intrinsic != null) {
return Collections.singletonList(intrinsic);
}
}
Collection<Tuple3<TerminalNode, ParserRuleContext, Integer>> vars = Collections.emptyList();
List<CodeElementModel> contextModels = new ArrayList<>();
List<ImportDeclarationModel> possibleImports = new ArrayList<>();
if (ctx.packageName() == null) {
contextModels.add(getFileModel().getPackage());
for (ImportDeclarationModel importDeclarationModel : getFileModel().getImportDeclarations()) {
if (importDeclarationModel.isMergeWithLocal()) {
possibleImports.add(importDeclarationModel);
}
}
ParseTree functionContext = getTopContext(parser, ctx, new IntervalSet() {{ add(GoParser.RULE_functionDecl); add(GoParser.RULE_methodDecl); }});
if (functionContext != null) {
vars = new ArrayList<>();
vars.addAll(localsAnalyzer.getReceiverParameters(functionContext));
vars.addAll(localsAnalyzer.getParameters(functionContext));
vars.addAll(localsAnalyzer.getReturnParameters(functionContext));
vars.addAll(localsAnalyzer.getConstants(functionContext));
vars.addAll(localsAnalyzer.getLocals(functionContext));
}
} else {
String pkgName = ctx.packageName().IDENTIFIER().getSymbol().getText();
for (ImportDeclarationModel importDeclarationModel : getFileModel().getImportDeclarations()) {
if (!importDeclarationModel.isMergeWithLocal() && pkgName.equals(importDeclarationModel.getName())) {
possibleImports.add(importDeclarationModel);
}
}
}
PackageModel builtinPackage = CodeModelCacheImpl.getInstance().getUniquePackage(getFileModel().getPackage().getProject(), "builtin");
if (builtinPackage != null) {
contextModels.add(builtinPackage);
}
for (ImportDeclarationModel importDeclarationModel : possibleImports) {
Collection<? extends PackageModel> resolved = CodeModelCacheImpl.getInstance().resolvePackages(importDeclarationModel);
if (resolved != null) {
contextModels.addAll(resolved);
}
}
Set<CodeElementModel> members = new HashSet<>();
TerminalNode nameNode = ctx.IDENTIFIER();
if (nameNode != null) {
String name = nameNode.getSymbol().getText();
for (Tuple3<TerminalNode, ParserRuleContext, Integer> entry : vars) {
if (ParseTrees.isAncestorOf(entry.getItem2(), nameNode)) {
// prevent stack overflow from redeclaration of a variable
continue;
}
if (!name.equals(entry.getItem1().getText())) {
continue;
}
Collection<? extends CodeElementModel> varTypes = visit(entry.getItem2());
ArrayList<CodeElementModel> unbundledTypes = new ArrayList<>();
for (CodeElementModel varType : varTypes) {
if (varType instanceof BundledReturnTypeModel) {
List<? extends CodeElementModel> returnValues = ((BundledReturnTypeModel)varType).getReturnValues();
if (returnValues.size() > entry.getItem3()) {
unbundledTypes.add(((BundledReturnTypeModel)varType).getReturnValues().get(entry.getItem3()));
}
} else if (entry.getItem3() == 0) {
unbundledTypes.add(varType);
}
}
varTypes = unbundledTypes;
if (varTypes.isEmpty()) {
varTypes = Collections.singleton(new UnknownTypeModelImpl(getFileModel()));
}
for (CodeElementModel unresolvedVarType : varTypes) {
for (TypeModelImpl varType : resolveType(unresolvedVarType, false, false)) {
// TODO: use proper var kind
VarModelImpl varModel = new VarModelImpl(name, VarKind.LOCAL, varType, getFileModel(), entry.getItem1(), entry.getItem2());
members.add(varModel);
}
}
}
if (members.isEmpty()) {
for (CodeElementModel model : contextModels) {
members.addAll(model.getMembers(name));
}
}
}
setTargetProperty(ctx, members);
return members;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_type, version=0, dependents=Dependents.SELF),
})
public Collection<? extends CodeElementModel> visitTypeAssertionExpr(TypeAssertionExprContext ctx) {
Collection<? extends CodeElementModel> types = visit(ctx.type());
if (types.isEmpty()) {
return types;
}
List<CodeElementModel> assertionBundles = new ArrayList<>();
for (CodeElementModel model : types) {
if (model instanceof AbstractCodeElementModel) {
assertionBundles.add(new BundledReturnTypeModel(Arrays.asList((AbstractCodeElementModel)model, (AbstractCodeElementModel)IntrinsicTypeModels.BOOL)));
} else {
assertionBundles.add(model);
}
}
setTargetProperty(ctx, assertionBundles);
return assertionBundles;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_literal, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_basicLiteral, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_compositeLiteral, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionLiteral, version=0),
})
public Collection<? extends CodeElementModel> visitLiteral(LiteralContext ctx) {
Collection<? extends CodeElementModel> result;
if (ctx.basicLiteral() != null) {
result = visit(ctx.basicLiteral());
} else if (ctx.compositeLiteral() != null) {
result = visit(ctx.compositeLiteral());
} else if (ctx.functionLiteral() != null) {
result = visit(ctx.functionLiteral());
} else {
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_basicLiteral, version=0)
public Collection<? extends CodeElementModel> visitBasicLiteral(BasicLiteralContext ctx) {
Collection<? extends CodeElementModel> result;
if (ctx.INT_LITERAL() != null) {
result = Collections.singletonList(IntrinsicTypeModels.INT);
} else if (ctx.FLOAT_LITERAL() != null) {
result = Collections.singletonList(IntrinsicTypeModels.FLOAT32);
} else if (ctx.IMAGINARY_LITERAL() != null) {
result = Collections.singletonList(IntrinsicTypeModels.COMPLEX64);
} else if (ctx.CharLiteral() != null) {
result = Collections.singletonList(IntrinsicTypeModels.INT32);
} else if (ctx.StringLiteral() != null) {
result = Collections.singletonList(IntrinsicTypeModels.STRING);
} else {
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_compositeLiteral, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_literalType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_structType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_arrayType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_sliceType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_mapType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeName, version=0, dependents=Dependents.SELF),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_elementType, version=0),
})
public Collection<? extends CodeElementModel> visitCompositeLiteral(CompositeLiteralContext ctx) {
Collection<? extends CodeElementModel> result;
LiteralTypeContext literalType = ctx.literalType();
if (literalType != null) {
if (literalType.structType() != null) {
result = visit(literalType.structType());
} else if (literalType.arrayType() != null) {
result = visit(literalType.arrayType());
} else if (literalType.sliceType() != null) {
result = visit(literalType.sliceType());
} else if (literalType.mapType() != null) {
result = visit(literalType.mapType());
} else if (literalType.typeName() != null) {
result = visit(literalType.typeName());
} else if (literalType.elementType() != null) {
List<CodeElementModel> arrayElementType = new ArrayList<>();
arrayElementType.addAll(visit(literalType.elementType()));
for (int i = 0; i < arrayElementType.size(); i++) {
CodeElementModel elementType = arrayElementType.get(i);
if (elementType instanceof TypeModelImpl) {
// eventually array types will have their length embedded
arrayElementType.set(i, new TypeArrayModelImpl((TypeModelImpl)elementType));
}
}
result = arrayElementType;
} else {
result = Collections.emptyList();
}
} else {
result = Collections.emptyList();
}
setTargetProperty(ctx, result);
return result;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionLiteral, version=0)
public Collection<? extends CodeElementModel> visitFunctionLiteral(FunctionLiteralContext ctx) {
return Collections.emptyList();
}
@NonNull
private Collection<? extends TypeModelImpl> resolveType(@NonNull CodeElementModel model, boolean lookThroughAliases, boolean dereferenceArrayPointers) {
TypeModelImpl type;
if (model instanceof TypeModelImpl) {
type = (TypeModelImpl)model;
} else if (model instanceof VarModelImpl) {
type = ((VarModelImpl)model).getVarType();
} else {
LOGGER.log(Level.WARNING, "Cannot resolve target type from model {0}.", model.getClass());
return Collections.emptyList();
}
Collection<? extends TypeModelImpl> resolved = type.resolve();
if (lookThroughAliases) {
List<TypeModelImpl> result = new ArrayList<>();
for (TypeModelImpl typeModelImpl : resolved) {
if (typeModelImpl instanceof TypeAliasModel) {
result.addAll(resolveType(((TypeAliasModel)typeModelImpl).getType(), lookThroughAliases, dereferenceArrayPointers));
} else {
result.add(typeModelImpl);
}
}
resolved = result;
}
if (dereferenceArrayPointers) {
List<TypeModelImpl> result = new ArrayList<>();
for (TypeModelImpl typeModelImpl : resolved) {
if (typeModelImpl instanceof TypePointerModel) {
Collection<? extends TypeModelImpl> dereferences = resolveType(((TypePointerModel)typeModelImpl).getElementType(), lookThroughAliases, false);
for (TypeModelImpl dereference : dereferences) {
if (dereference instanceof TypeArrayModel) {
result.add(dereference);
}
}
} else {
result.add(typeModelImpl);
}
}
resolved = result;
}
return resolved;
}
private Collection<? extends CodeElementModel> getTargetProperty(ParseTree context) {
return annotations.getProperty(context, ATTR_TARGET);
}
private void setTargetProperty(ParseTree context, @NonNull Collection<? extends CodeElementModel> models) {
Parameters.notNull("models", models);
annotations.putProperty(context, ATTR_TARGET, models);
}
}
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageName, version=0)
private Collection<? extends CodeElementModel> resolveSelectorTarget(ParserRuleContext qualifier, GoAnnotatedParseTree annotatedParseTree, PackageModel currentPackage, Map<String, Collection<PackageModel>> resolvedPackages) {
if (qualifier == null) {
// don't have the information necessary to resolve
return Collections.emptyList();
}
ObjectDecorator<Tree> treeDecorator = annotatedParseTree.getTreeDecorator();
Collection<? extends CodeElementModel> resolvedQualifier = treeDecorator.getProperty(qualifier, GoAnnotations.MODELS);
if (resolvedQualifier == null) {
CodeElementReference qualifierCodeClass = treeDecorator.getProperty(qualifier, GoAnnotations.CODE_CLASS);
if (qualifierCodeClass != CodeElementReference.MISSING) {
resolvedQualifier = qualifierCodeClass.resolve(annotatedParseTree, currentPackage, resolvedPackages);
}
}
if (resolvedQualifier == null) {
CodeElementReference qualifierExprType = treeDecorator.getProperty(qualifier, GoAnnotations.EXPR_TYPE);
if (qualifierExprType != CodeElementReference.MISSING) {
resolvedQualifier = qualifierExprType.resolve(annotatedParseTree, currentPackage, resolvedPackages);
}
}
if (resolvedQualifier == null) {
NodeType qualifierNodeType = treeDecorator.getProperty(qualifier, GoAnnotations.NODE_TYPE);
if (qualifierNodeType == NodeType.UNDEFINED) {
if (treeDecorator.getProperty(qualifier, GoAnnotations.QUALIFIED_EXPR)) {
// haven't resolved the qualifier, which is itself a qualified expression
return Collections.emptyList();
}
TerminalNode unqualifiedLink = treeDecorator.getProperty(qualifier, GoAnnotations.UNQUALIFIED_LINK);
if (unqualifiedLink != null) {
qualifierNodeType = treeDecorator.getProperty(qualifier, GoAnnotations.NODE_TYPE);
if (qualifierNodeType == NodeType.UNDEFINED) {
qualifierNodeType = NodeType.UNKNOWN;
}
} else {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Unable to resolve unqualified link from qualifier: {0}", qualifier.toString(Arrays.asList(GoParser.ruleNames)));
}
qualifierNodeType = NodeType.UNKNOWN;
}
}
assert qualifierNodeType != NodeType.UNDEFINED;
if (qualifierNodeType == NodeType.UNKNOWN) {
// can't resolve a dereference if the qualifier couldn't be resolved
// tokenDecorator.putProperty(token, GoAnnotations.NODE_TYPE, NodeType.UNKNOWN);
// treeDecorator.putProperty(node, GoAnnotations.NODE_TYPE, NodeType.UNKNOWN);
return Collections.emptyList();
}
if (qualifierNodeType == NodeType.TYPE_LITERAL) {
throw new UnsupportedOperationException("Not yet implemented");
//return resolveQualifierType(qualifier, currentPackage, resolvedPackages);
} else if (qualifierNodeType == NodeType.PACKAGE_REF) {
assert qualifier instanceof PackageNameContext;
String packageName = ((PackageNameContext)qualifier).IDENTIFIER().getSymbol().getText();
resolvedQualifier = resolvedPackages.get(packageName);
if (resolvedQualifier == null) {
resolvedQualifier = Collections.emptyList();
}
} else if (qualifierNodeType == NodeType.VAR_REF) {
// must be referring to something within the current file since it's resolved internally
TerminalNode target = treeDecorator.getProperty(qualifier, GoAnnotations.LOCAL_TARGET);
assert target != null && treeDecorator.getProperty(target, GoAnnotations.NODE_TYPE) == NodeType.VAR_DECL;
ParserRuleContext explicitType = treeDecorator.getProperty(qualifier, GoAnnotations.EXPLICIT_TYPE);
if (explicitType == null && target != null) {
explicitType = treeDecorator.getProperty(target, GoAnnotations.EXPLICIT_TYPE);
}
if (explicitType != null) {
LOGGER.log(Level.WARNING, "Unable to resolve explicit type for qualifier: {0}", qualifier);
resolvedQualifier = Collections.emptyList();
} else {
ParserRuleContext implicitType = target != null ? treeDecorator.getProperty(target, GoAnnotations.IMPLICIT_TYPE) : null;
int implicitIndex = target != null ? treeDecorator.getProperty(target, GoAnnotations.IMPLICIT_INDEX) : -1;
LOGGER.log(Level.WARNING, "Unable to resolve implicit type for qualifier: {0}", qualifier);
resolvedQualifier = Collections.emptyList();
}
} else {
LOGGER.log(Level.WARNING, "Unable to resolve qualifier: {0}", qualifier);
resolvedQualifier = Collections.emptyList();
}
}
assert resolvedQualifier != null;
if (resolvedQualifier == null) {
LOGGER.log(Level.WARNING, "Should not have a null resolved qualifier at this point.");
resolvedQualifier = Collections.emptyList();
}
return resolvedQualifier;
//List<CodeElementModel> qualifiedModels = new ArrayList<CodeElementModel>();
//for (CodeElementModel model : resolvedQualifier) {
// qualifiedModels.addAll(SemanticAnalyzer.getSelectableMembers(model));
//}
//
//return qualifiedModels;
}
private GoAnnotatedParseTree getAnnotatedParseTree(VersionedDocument document, ParseTree parseTree, Map<ParseTree, GoAnnotatedParseTree> annotatedParseTrees) {
GoAnnotatedParseTree annotatedParseTree = annotatedParseTrees.get(parseTree);
if (annotatedParseTree == null) {
annotatedParseTree = SemanticAnalyzer.analyze(document, parseTree);
annotatedParseTrees.put(parseTree, annotatedParseTree);
}
return annotatedParseTree;
}
}
private static class ScopeContextVisitor extends GoParserBaseVisitor<ParseTree> {
public static ScopeContextVisitor INSTANCE = new ScopeContextVisitor();
private static ScopeContextVisitorHelper HELPER_INSTANCE = new ScopeContextVisitorHelper();
protected ScopeContextVisitor() {
}
@Override
public ParseTree visitTerminal(TerminalNode node) {
return visit(node.getParent());
}
@Override
protected ParseTree defaultResult() {
throw new UnsupportedOperationException();
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_baseTypeName, version=0)
public ParseTree visitBaseTypeName(BaseTypeNameContext ctx) {
// this is not a declaration
return null;
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_builtinCall, version=0)
public ParseTree visitBuiltinCall(BuiltinCallContext ctx) {
// this is not a declaration
return null;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_topLevelDecl, version=0),
})
public ParseTree visitFunctionDecl(FunctionDeclContext ctx) {
return HELPER_INSTANCE.visit((TopLevelDeclContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_identifierList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_constSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_constDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_declaration, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_fieldDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_structType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_shortVarDecl, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_varSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_varDecl, version=0),
})
public ParseTree visitIdentifierList(IdentifierListContext ctx) {
switch (ctx.getParent().getRuleIndex()) {
case GoParser.RULE_constSpec:
ConstDeclContext constDeclContext = (ConstDeclContext)((ConstSpecContext)ctx.getParent()).getParent();
return HELPER_INSTANCE.visit((DeclarationContext)constDeclContext.getParent());
case GoParser.RULE_fieldDecl:
return (StructTypeContext)((FieldDeclContext)ctx.getParent()).getParent();
case GoParser.RULE_parameterDecl:
return HELPER_INSTANCE.visit((ParameterDeclContext)ctx.getParent());
case GoParser.RULE_shortVarDecl:
return HELPER_INSTANCE.visit((ShortVarDeclContext)ctx.getParent());
case GoParser.RULE_varSpec:
VarSpecContext varSpecContext = (VarSpecContext)ctx.getParent();
VarDeclContext varDeclContext = (VarDeclContext)varSpecContext.getParent();
return HELPER_INSTANCE.visit((DeclarationContext)varDeclContext.getParent());
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_label, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_labeledStmt, version=0),
})
public ParseTree visitLabel(LabelContext ctx) {
if (ctx.getParent().getRuleIndex() != GoParser.RULE_labeledStmt) {
// not a declaration
return null;
}
return HELPER_INSTANCE.visit((LabeledStmtContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodExpr, version=0),
})
public ParseTree visitMethodName(MethodNameContext ctx) {
switch (ctx.getParent().getRuleIndex()) {
case GoParser.RULE_methodDecl:
return HELPER_INSTANCE.visit((MethodDeclContext)ctx.getParent());
case GoParser.RULE_methodSpec:
return HELPER_INSTANCE.visit((MethodSpecContext)ctx.getParent());
case GoParser.RULE_methodExpr:
// this isn't a declaration
return null;
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageName, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_importSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_importDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_packageClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_sourceFileBody, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0),
})
public ParseTree visitPackageName(PackageNameContext ctx) {
switch (ctx.getParent().getRuleIndex()) {
case GoParser.RULE_importSpec:
ImportSpecContext importSpecContext = (ImportSpecContext)ctx.getParent();
ImportDeclContext importDeclContext = (ImportDeclContext)importSpecContext.getParent();
return (SourceFileBodyContext)importDeclContext.getParent();
case GoParser.RULE_packageClause:
return (SourceFileBodyContext)((PackageClauseContext)ctx.getParent()).getParent();
case GoParser.RULE_qualifiedIdentifier:
// not a declaration
return null;
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_qualifiedIdentifier, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_operand, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_rangeClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_forStmt, version=0),
})
public ParseTree visitQualifiedIdentifier(QualifiedIdentifierContext ctx) {
if (ctx.getParent().getRuleIndex() != GoParser.RULE_operand) {
// not a declaration
return null;
}
OperandContext operandContext = (OperandContext)ctx.getParent();
OperandExprContext operandExprContext = (OperandExprContext)operandContext.getParent();
if (operandExprContext.getParent().getRuleIndex() != GoParser.RULE_rangeClause) {
// not a declaration
return null;
}
RangeClauseContext rangeClauseContext = (RangeClauseContext)operandExprContext.getParent();
// just check eq... both operators missing is an ambig situation
if (rangeClauseContext.eq != null) {
// not a declaration
return null;
}
if (operandExprContext != rangeClauseContext.e1 && operandExprContext != rangeClauseContext.e2) {
// not a declaration
return null;
}
return (ForStmtContext)rangeClauseContext.getParent();
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_receiver, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=0),
})
public ParseTree visitReceiver(ReceiverContext ctx) {
return (MethodDeclContext)ctx.getParent();
}
@Override
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_expression, version=1)
public ParseTree visitSelectorExpr(SelectorExprContext ctx) {
// this isn't a declaration
return null;
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeDecl, version=0),
})
public ParseTree visitTypeSpec(TypeSpecContext ctx) {
return HELPER_INSTANCE.visit((TypeDeclContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchGuard, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchStmt, version=0),
})
public ParseTree visitTypeSwitchGuard(TypeSwitchGuardContext ctx) {
return (TypeSwitchStmtContext)ctx.getParent();
}
private static class ScopeContextVisitorHelper extends ScopeContextVisitor {
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_topLevelDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_sourceFileBody, version=1),
})
public ParseTree visitTopLevelDecl(TopLevelDeclContext ctx) {
return (SourceFileBodyContext)ctx.getParent();
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_topLevelDecl, version=0),
})
public ParseTree visitMethodDecl(MethodDeclContext ctx) {
return visit((TopLevelDeclContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_interfaceType, version=0),
})
public ParseTree visitMethodSpec(MethodSpecContext ctx) {
return (InterfaceTypeContext)ctx.getParent();
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_declaration, version=0),
})
public ParseTree visitTypeDecl(TypeDeclContext ctx) {
return visit((DeclarationContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_declaration, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_topLevelDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_statement, version=0),
})
public ParseTree visitDeclaration(DeclarationContext ctx) {
switch (ctx.getParent().getRuleIndex()) {
case GoParser.RULE_topLevelDecl:
return visit((TopLevelDeclContext)ctx.getParent());
case GoParser.RULE_statement:
return visit((StatementContext)ctx.getParent());
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_shortVarDecl, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_simpleStmt, version=1),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_statement, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_ifStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_exprSwitchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_initStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_forClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_forStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_postStmt, version=0),
})
public ParseTree visitShortVarDecl(ShortVarDeclContext ctx) {
SimpleStmtContext simpleStmtContext = (SimpleStmtContext)ctx.getParent();
switch (simpleStmtContext.getParent().getRuleIndex()) {
case GoParser.RULE_statement:
return HELPER_INSTANCE.visit((StatementContext)simpleStmtContext.getParent());
case GoParser.RULE_ifStmt:
return (IfStmtContext)simpleStmtContext.getParent();
case GoParser.RULE_exprSwitchStmt:
return (ExprSwitchStmtContext)simpleStmtContext.getParent();
case GoParser.RULE_typeSwitchStmt:
return (TypeSwitchStmtContext)simpleStmtContext.getParent();
case GoParser.RULE_initStmt:
InitStmtContext initStmtContext = (InitStmtContext)simpleStmtContext.getParent();
ForClauseContext forClauseContext = (ForClauseContext)initStmtContext.getParent();
return (ForStmtContext)forClauseContext.getParent();
case GoParser.RULE_postStmt:
PostStmtContext postStmtContext = (PostStmtContext)simpleStmtContext.getParent();
forClauseContext = (ForClauseContext)postStmtContext.getParent();
return (ForStmtContext)forClauseContext.getParent();
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameterList, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_parameters, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_signature, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_result, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionType, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodSpec, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_functionDecl, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_methodDecl, version=0),
})
public ParseTree visitParameterDecl(ParameterDeclContext ctx) {
ParameterListContext parameterListContext = (ParameterListContext)ctx.getParent();
ParametersContext parametersContext = (ParametersContext)parameterListContext.getParent();
SignatureContext signatureContext;
if (parametersContext.getParent().getRuleIndex() == GoParser.RULE_signature) {
signatureContext = (SignatureContext)parametersContext.getParent();
} else {
ResultContext resultContext = (ResultContext)parametersContext.getParent();
signatureContext = (SignatureContext)resultContext.getParent();
}
switch (signatureContext.getParent().getRuleIndex()) {
case GoParser.RULE_functionType:
return (FunctionTypeContext)signatureContext.getParent();
case GoParser.RULE_methodSpec:
return (MethodSpecContext)signatureContext.getParent();
case GoParser.RULE_functionDecl:
return (FunctionDeclContext)signatureContext.getParent();
case GoParser.RULE_methodDecl:
return (MethodDeclContext)signatureContext.getParent();
default:
throw new UnsupportedOperationException();
}
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_labeledStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_statement, version=0),
})
public ParseTree visitLabeledStmt(LabeledStmtContext ctx) {
return visit((StatementContext)ctx.getParent());
}
@Override
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_statement, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_block, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_labeledStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_commClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_selectStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_exprCaseClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_exprSwitchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeCaseClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=GoParser.RULE_typeSwitchStmt, version=0),
})
public ParseTree visitStatement(StatementContext ctx) {
switch (ctx.getParent().getRuleIndex()) {
case GoParser.RULE_block:
return (BlockContext)ctx.getParent();
case GoParser.RULE_labeledStmt:
return visit((LabeledStmtContext)ctx.getParent());
case GoParser.RULE_commClause:
return (SelectStmtContext)((CommClauseContext)ctx.getParent()).getParent();
case GoParser.RULE_exprCaseClause:
return (ExprSwitchStmtContext)((ExprCaseClauseContext)ctx.getParent()).getParent();
case GoParser.RULE_typeCaseClause:
return (TypeSwitchStmtContext)((TypeCaseClauseContext)ctx.getParent()).getParent();
default:
throw new UnsupportedOperationException();
}
}
}
}
public static class UnknownTypeModelImpl extends TypeModelImpl {
public UnknownTypeModelImpl(FileModelImpl fileModel) {
this("?", fileModel);
}
public UnknownTypeModelImpl(String name, FileModelImpl fileModel) {
super(name, fileModel, null, null);
}
@Override
public Collection<? extends TypeModelImpl> resolve() {
return Collections.singletonList(this);
}
@Override
public boolean isResolved() {
return true;
}
@Override
public TypeKind getKind() {
return TypeKind.UNKNOWN;
}
@Override
public String getSimpleName() {
return "?";
}
@Override
public Collection<? extends AbstractCodeElementModel> getMembers() {
return Collections.emptyList();
}
}
}