diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml
index bf0b6bc90..b7ff317ed 100644
--- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml
+++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_1_1_.xml
@@ -22,9 +22,6 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml
index 3a54aa52a..5336002e2 100644
--- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml
+++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_3_4_.xml
@@ -30,9 +30,6 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml
index 8f835574d..83723830f 100644
--- a/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml
+++ b/.idea/runConfigurations/org_elixir_lang__ELIXIR_VERSION_1_4_0_.xml
@@ -22,9 +22,6 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index f44bfe5d7..a37608746 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -68,9 +68,9 @@ before_install:
- tar xvfz jdk-8u112-linux-x64.tar.gz
- export JAVA_HOME=${PWD}/jdk1.8.0_112
- export PATH=${JAVA_HOME}/bin:${PATH}
-- wget http://www-us.apache.org/dist//ant/binaries/apache-ant-1.10.0-bin.tar.gz
-- tar xvfz apache-ant-1.10.0-bin.tar.gz
-- export PATH=${PWD}/apache-ant-1.10.0/bin:${PATH}
+- wget http://www-us.apache.org/dist//ant/binaries/apache-ant-1.10.1-bin.tar.gz
+- tar xvfz apache-ant-1.10.1-bin.tar.gz
+- export PATH=${PWD}/apache-ant-1.10.1/bin:${PATH}
- java -version
- ant -version
install:
diff --git a/src/org/elixir_lang/annonator/Callable.java b/src/org/elixir_lang/annonator/Callable.java
index 14b18d5f4..d638e12c7 100644
--- a/src/org/elixir_lang/annonator/Callable.java
+++ b/src/org/elixir_lang/annonator/Callable.java
@@ -11,6 +11,7 @@
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import org.elixir_lang.ElixirSyntaxHighlighter;
+import org.elixir_lang.errorreport.Logger;
import org.elixir_lang.psi.call.Call;
import org.jetbrains.annotations.NotNull;
@@ -66,7 +67,15 @@ private void visitCall(@NotNull final Call call) {
if (reference instanceof PsiPolyVariantReference) {
PsiPolyVariantReference polyVariantReference = (PsiPolyVariantReference) reference;
- ResolveResult[] resolveResults = polyVariantReference.multiResolve(false);
+ ResolveResult[] resolveResults;
+
+ try {
+ resolveResults = polyVariantReference.multiResolve(false);
+ } catch (StackOverflowError stackOverflowError) {
+ Logger.error(Callable.class, "StackOverflowError when annotating Call", call);
+ resolveResults = new ResolveResult[0];
+ }
+
List validResolveResults = ContainerUtil.filter(
resolveResults,
new Condition() {
diff --git a/src/org/elixir_lang/annonator/ModuleAttribute.java b/src/org/elixir_lang/annonator/ModuleAttribute.java
index ad3dde9d6..dd1eb608f 100644
--- a/src/org/elixir_lang/annonator/ModuleAttribute.java
+++ b/src/org/elixir_lang/annonator/ModuleAttribute.java
@@ -32,6 +32,7 @@
import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.identifierName;
import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.stripAccessExpression;
import static org.elixir_lang.reference.ModuleAttribute.*;
+import static org.elixir_lang.structure_view.element.CallDefinitionHead.stripAllOuterParentheses;
/**
* Annotates module attributes.
@@ -578,9 +579,14 @@ private void highlightSpecification(AtUnqualifiedNoParenthesesCall atUnqualified
if (leftOperand instanceof Type) {
Type typeOperation = (Type) leftOperand;
PsiElement typeOperationLeftOperand = typeOperation.leftOperand();
+ PsiElement strippedTypeOperationLeftOperand = null;
- if (typeOperationLeftOperand instanceof Call) {
- Call call = (Call) typeOperationLeftOperand;
+ if (typeOperationLeftOperand != null) {
+ strippedTypeOperationLeftOperand = stripAllOuterParentheses(typeOperationLeftOperand);
+ }
+
+ if (strippedTypeOperationLeftOperand instanceof Call) {
+ Call call = (Call) strippedTypeOperationLeftOperand;
PsiElement functionNameElement = call.functionNameElement();
if (functionNameElement != null) {
@@ -613,7 +619,7 @@ private void highlightSpecification(AtUnqualifiedNoParenthesesCall atUnqualified
);
}
} else {
- cannotHighlightTypes(typeOperationLeftOperand);
+ cannotHighlightTypes(strippedTypeOperationLeftOperand);
}
PsiElement matchedTypeOperationRightOperand = typeOperation.rightOperand();
diff --git a/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java b/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java
index e06adff79..734d381f4 100644
--- a/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java
+++ b/src/org/elixir_lang/code_insight/lookup/element_renderer/CallDefinitionClause.java
@@ -1,10 +1,13 @@
package org.elixir_lang.code_insight.lookup.element_renderer;
+import com.google.common.base.Joiner;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
+import com.intellij.diagnostic.LogMessageEx;
import com.intellij.navigation.ItemPresentation;
+import com.intellij.openapi.diagnostic.Attachment;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiElement;
-import org.elixir_lang.errorreport.Logger;
import org.elixir_lang.psi.call.Call;
import org.jetbrains.annotations.NotNull;
@@ -35,37 +38,98 @@ public void renderElement(LookupElement element, LookupElementPresentation prese
PsiElement psiElement = element.getPsiElement();
- assert psiElement != null;
-
- if (psiElement instanceof Call) {
- Call call = (Call) psiElement;
+ if (psiElement == null) {
+ renderObject(element);
+ } else {
+ renderPsiElement(psiElement, presentation);
+ }
+ }
- if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) {
- org.elixir_lang.structure_view.element.CallDefinitionClause structureView =
- org.elixir_lang.structure_view.element.CallDefinitionClause.fromCall(call);
+ /*
+ * Private Instance Methods
+ */
- if (structureView != null) {
- ItemPresentation structureViewPresentation = structureView.getPresentation();
+ private void renderCall(@NotNull Call call, @NotNull LookupElementPresentation presentation) {
+ if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) {
+ renderCallDefinitionClause(call, presentation);
+ }
+ }
- presentation.setIcon(structureViewPresentation.getIcon(true));
- String presentableText = structureViewPresentation.getPresentableText();
+ private void renderCallDefinitionClause(@NotNull Call call, @NotNull LookupElementPresentation presentation) {
+ org.elixir_lang.structure_view.element.CallDefinitionClause structureView =
+ org.elixir_lang.structure_view.element.CallDefinitionClause.fromCall(call);
- if (presentableText != null) {
- int nameLength = name.length();
- int presentableTextLength = presentableText.length();
+ if (structureView != null) {
+ renderStructureView(structureView, presentation);
+ }
+ }
- if (nameLength <= presentableTextLength) {
- presentation.appendTailText(presentableText.substring(nameLength), true);
- }
- }
+ private void renderItemPresentation(@NotNull ItemPresentation itemPresentation,
+ @NotNull LookupElementPresentation lookupElementPresentation) {
+ lookupElementPresentation.setIcon(itemPresentation.getIcon(true));
+ String presentableText = itemPresentation.getPresentableText();
- String locationString = structureViewPresentation.getLocationString();
+ if (presentableText != null) {
+ int nameLength = name.length();
+ int presentableTextLength = presentableText.length();
- if (locationString != null) {
- presentation.appendTailText(" (" + locationString + ")", false);
- }
- }
+ if (nameLength <= presentableTextLength) {
+ lookupElementPresentation.appendTailText(presentableText.substring(nameLength), true);
}
}
+
+ String locationString = itemPresentation.getLocationString();
+
+ if (locationString != null) {
+ lookupElementPresentation.appendTailText(" (" + locationString + ")", false);
+ }
+ }
+
+ private void renderObject(@NotNull LookupElement lookupElement) {
+ Logger logger = Logger.getInstance(CallDefinitionClause.class);
+ Object object = lookupElement.getObject();
+ String userMessage = "CallDefinitionClause render called on LookupElement with null getPsiElement\n" +
+ "## name\n" +
+ "\n" +
+ "```\n" +
+ name + "\n" +
+ "```\n" +
+ "\n" +
+ "## getObject()\n"+
+ "\n" +
+ "### toString()\n" +
+ "\n" +
+ "```\n" +
+ object.toString() + "\n" +
+ "```\n" +
+ "\n" +
+ "### getClass().getName()\n" +
+ "\n" +
+ "```\n" +
+ object.getClass().getName() + "\n" +
+ "```\n";
+
+ String details = Joiner.on("\n").join(new Throwable().getStackTrace());
+ String title = "CallDefinitionClause render called on LookupElement with null getPsiElement";
+ logger.error(
+ LogMessageEx.createEvent(
+ userMessage,
+ details,
+ title,
+ null,
+ (Attachment) null
+ )
+ );
+ }
+
+ private void renderPsiElement(@NotNull PsiElement psiElement, @NotNull LookupElementPresentation presentation) {
+ if (psiElement instanceof Call) {
+ renderCall((Call) psiElement, presentation);
+ }
+ }
+
+ private void renderStructureView(@NotNull org.elixir_lang.structure_view.element.CallDefinitionClause structureView,
+ @NotNull LookupElementPresentation presentation) {
+ renderItemPresentation(structureView.getPresentation(), presentation);
}
}
diff --git a/src/org/elixir_lang/errorreport/Logger.java b/src/org/elixir_lang/errorreport/Logger.java
index 989cdef8b..5e0357f3f 100644
--- a/src/org/elixir_lang/errorreport/Logger.java
+++ b/src/org/elixir_lang/errorreport/Logger.java
@@ -112,6 +112,14 @@ private static String excerpt(@NotNull PsiFile containingFile, @NotNull PsiEleme
excerptBuilder.append(startingLine);
excerptBuilder.append('-');
excerptBuilder.append(endingLine);
+
+ VirtualFile virtualFile = containingFile.getVirtualFile();
+
+ if (virtualFile != null) {
+ excerptBuilder.append(" in ");
+ excerptBuilder.append(virtualFile.getPath());
+ }
+
excerptBuilder.append("\n");
}
diff --git a/src/org/elixir_lang/psi/Import.java b/src/org/elixir_lang/psi/Import.java
index 8359a8b71..f8072aaac 100644
--- a/src/org/elixir_lang/psi/Import.java
+++ b/src/org/elixir_lang/psi/Import.java
@@ -60,7 +60,14 @@ public Boolean fun(@NotNull @SuppressWarnings("unused") Call call) {
*/
public static void callDefinitionClauseCallWhile(@NotNull Call importCall,
@NotNull final Function function) {
- Call modularCall = modular(importCall);
+ Call modularCall;
+
+ try {
+ modularCall = modular(importCall);
+ } catch (StackOverflowError stackOverflowError) {
+ Logger.error(Import.class, "StackvoerflowError while finding modular for import", importCall);
+ modularCall = null;
+ }
if (modularCall != null) {
final Function optionsFilter = callDefinitionClauseCallFilter(importCall);
diff --git a/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java b/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java
index 15a0c0932..62c12dc4f 100644
--- a/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java
+++ b/src/org/elixir_lang/psi/impl/ElixirPsiImplUtil.java
@@ -4907,6 +4907,25 @@ public static OtpErlangObject quotedVariable(@NotNull final OtpErlangObject quot
);
}
+ @Contract(pure = true)
+ @NotNull
+ public static boolean recursiveKernelImport(@NotNull QualifiableAlias qualifiableAlias,
+ @NotNull PsiElement maxScope) {
+ boolean recursiveKernelImport = false;
+
+ if (maxScope instanceof ElixirFile) {
+ ElixirFile elixirFile = (ElixirFile) maxScope;
+
+ if (elixirFile.getName().equals("kernel.ex")) {
+ String qualifiableAliasName = qualifiableAlias.getName();
+
+ recursiveKernelImport = qualifiableAliasName != null && qualifiableAliasName.equals(KERNEL);
+ }
+ }
+
+ return recursiveKernelImport;
+ }
+
@Contract(pure = true)
@NotNull
public static int resolvedFinalArity(@NotNull final Call call) {
@@ -5631,10 +5650,13 @@ public static Call maybeAliasToModular(@NotNull final PsiElement maybeAlias, @No
if (maybeQualifiableAlias instanceof QualifiableAlias) {
QualifiableAlias qualifiableAlias = (QualifiableAlias) maybeQualifiableAlias;
- /* need to construct reference directly as qualified aliases don't return a
- reference except for the outermost */
- PsiPolyVariantReference reference = new org.elixir_lang.reference.Module(qualifiableAlias, maxScope);
- modular = aliasToModular(qualifiableAlias, reference);
+
+ if (!recursiveKernelImport(qualifiableAlias, maxScope)) {
+ /* need to construct reference directly as qualified aliases don't return a reference except for the
+ outermost */
+ PsiPolyVariantReference reference = new org.elixir_lang.reference.Module(qualifiableAlias, maxScope);
+ modular = aliasToModular(qualifiableAlias, reference);
+ }
}
return modular;
diff --git a/src/org/elixir_lang/psi/scope/CallDefinitionClause.java b/src/org/elixir_lang/psi/scope/CallDefinitionClause.java
index 6880a390f..06df86d99 100644
--- a/src/org/elixir_lang/psi/scope/CallDefinitionClause.java
+++ b/src/org/elixir_lang/psi/scope/CallDefinitionClause.java
@@ -5,6 +5,7 @@
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.util.Function;
+import org.elixir_lang.errorreport.Logger;
import org.elixir_lang.psi.Import;
import org.elixir_lang.psi.call.Call;
import org.elixir_lang.structure_view.element.modular.Module;
@@ -80,15 +81,19 @@ protected boolean execute(@NotNull Call element, @NotNull final ResolveState sta
} else if (Import.is(element)) {
final ResolveState importState = state.put(IMPORT_CALL, element);
- Import.callDefinitionClauseCallWhile(
- element,
- new Function() {
- @Override
- public Boolean fun(Call callDefinitionClause) {
- return executeOnCallDefinitionClause(callDefinitionClause, importState);
+ try {
+ Import.callDefinitionClauseCallWhile(
+ element,
+ new Function() {
+ @Override
+ public Boolean fun(Call callDefinitionClause) {
+ return executeOnCallDefinitionClause(callDefinitionClause, importState);
+ }
}
- }
- );
+ );
+ } catch (StackOverflowError stackOverflowError) {
+ Logger.error(CallDefinitionClause.class, "StackOverflowError while processing import", element);
+ }
} else if (Module.is(element)) {
Call[] childCalls = macroChildCalls(element);
diff --git a/src/org/elixir_lang/psi/scope/module/MultiResolve.java b/src/org/elixir_lang/psi/scope/module/MultiResolve.java
index 12b8447cd..011a87023 100644
--- a/src/org/elixir_lang/psi/scope/module/MultiResolve.java
+++ b/src/org/elixir_lang/psi/scope/module/MultiResolve.java
@@ -1,5 +1,6 @@
package org.elixir_lang.psi.scope.module;
+import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
@@ -15,6 +16,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import static org.elixir_lang.Module.concat;
@@ -42,13 +44,21 @@ public static List resolveResultList(@NotNull String name,
private static Collection indexedNamedElements(@NotNull PsiNamedElement match,
@NotNull String unaliasedName) {
Project project = match.getProject();
- return StubIndex.getElements(
- AllName.KEY,
- unaliasedName,
- project,
- GlobalSearchScope.allScope(project),
- NamedElement.class
- );
+ Collection indexNamedElementCollection;
+
+ if (DumbService.isDumb(project)) {
+ indexNamedElementCollection = Collections.emptyList();
+ } else {
+ indexNamedElementCollection = StubIndex.getElements(
+ AllName.KEY,
+ unaliasedName,
+ project,
+ GlobalSearchScope.allScope(project),
+ NamedElement.class
+ );
+ }
+
+ return indexNamedElementCollection;
}
@Nullable
diff --git a/src/org/elixir_lang/reference/Module.java b/src/org/elixir_lang/reference/Module.java
index 32bc4045d..3fc3d25bf 100644
--- a/src/org/elixir_lang/reference/Module.java
+++ b/src/org/elixir_lang/reference/Module.java
@@ -1,6 +1,7 @@
package org.elixir_lang.reference;
import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
@@ -16,6 +17,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import static org.elixir_lang.reference.module.ResolvableName.resolvableName;
@@ -79,13 +81,19 @@ private List multiResolveProject(@NotNull Project project,
@NotNull String name) {
List results = new ArrayList();
- Collection namedElementCollection = StubIndex.getElements(
- AllName.KEY,
- name,
- project,
- GlobalSearchScope.allScope(project),
- NamedElement.class
- );
+ Collection namedElementCollection;
+
+ if (DumbService.isDumb(project)) {
+ namedElementCollection = Collections.emptyList();
+ } else {
+ namedElementCollection = StubIndex.getElements(
+ AllName.KEY,
+ name,
+ project,
+ GlobalSearchScope.allScope(project),
+ NamedElement.class
+ );
+ }
for (NamedElement namedElement : namedElementCollection) {
/* The NamedElement may be a ModuleImpl from a .beam. Using #getNaviationElement() ensures a source
diff --git a/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex b/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex
new file mode 100644
index 000000000..5f83968d3
--- /dev/null
+++ b/testData/org/elixir_lang/annotator/module_attribute/issue_605.ex
@@ -0,0 +1 @@
+@spec (+value) :: value when value: number
diff --git a/tests/org/elixir_lang/annotator/ModuleAttributeTest.java b/tests/org/elixir_lang/annotator/ModuleAttributeTest.java
index b66c49ddf..f0596c2c3 100644
--- a/tests/org/elixir_lang/annotator/ModuleAttributeTest.java
+++ b/tests/org/elixir_lang/annotator/ModuleAttributeTest.java
@@ -39,6 +39,11 @@ public void testIssue525() {
myFixture.checkHighlighting(false, false, true);
}
+ public void testIssue605() {
+ myFixture.configureByFile("issue_605.ex");
+ myFixture.checkHighlighting(false, false, true);
+ }
+
public void testMatch() {
myFixture.configureByFile("match.ex");
myFixture.checkHighlighting(false, false, true);