Permalink
Browse files

ErlangUndefinedCallbackFunction inspection added

  • Loading branch information...
1 parent 6eff030 commit 8d5fabb0ae46e3fd4ea3def2989b75f46734c70f @ignatov committed Mar 6, 2013
@@ -0,0 +1,5 @@
+<html>
+<body>
+Checks that the module implements all callbacks functions from behaviours.
+</body>
+</html>
@@ -260,6 +260,9 @@
<localInspection language="Erlang" shortName="ErlangIoFormat" displayName="io:format"
groupName="Erlang" enabledByDefault="true" level="WARNING"
implementationClass="org.intellij.erlang.inspection.ErlangIoFormatInspection"/>
+ <localInspection language="Erlang" shortName="ErlangUndefinedCallbackFunction" displayName="Undefined callback function"
+ groupName="Erlang" enabledByDefault="true" level="WARNING"
+ implementationClass="org.intellij.erlang.inspection.ErlangUndefinedCallbackFunctionInspection"/>
<spellchecker.bundledDictionaryProvider implementation="org.intellij.erlang.spellchecker.ErlangBundledDictionaryProvider"/>
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012-2013 Sergey Ignatov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.intellij.erlang.inspection;
+
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.intellij.erlang.psi.*;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author ignatov
+ */
+public class ErlangUndefinedCallbackFunctionInspection extends ErlangInspectionBase {
+ @Override
+ protected void checkFile(PsiFile file, ProblemsHolder problemsHolder) {
+ if (!(file instanceof ErlangFile)) return;
+
+ //noinspection unchecked
+ ErlangCompositeElement warningHolder = PsiTreeUtil.getChildOfAnyType(file, ErlangAttribute.class, ErlangModule.class);
+ if (warningHolder == null) return;
+
+ HashMap<String, String> needed = new HashMap<String, String>();
+
+ List<ErlangBehaviour> behaviours = ((ErlangFile) file).getBehaviours();
+ for (ErlangBehaviour behaviour : behaviours) {
+ ErlangModuleRef moduleRef = behaviour.getModuleRef();
+ PsiReference reference = moduleRef != null ? moduleRef.getReference() : null;
+ PsiElement resolve = reference != null ? reference.resolve() : null;
+
+ if (resolve instanceof ErlangModule) {
+ PsiFile containingFile = resolve.getContainingFile();
+ if (containingFile instanceof ErlangFile) {
+ Collection<String> fullNames = ((ErlangFile) containingFile).getAllCallbacksFullNames();
+ for (String name : fullNames) {
+ needed.put(name, ((ErlangModule) resolve).getName());
+ }
+ }
+ }
+ }
+
+ for (Map.Entry<String, String> entry : needed.entrySet()) {
+ String fullName = entry.getKey();
+ List<String> split = StringUtil.split(fullName, "/");
+ if (split.size() != 2) continue;
+ ErlangFunction function = ((ErlangFile) file).getFunction(split.get(0), StringUtil.parseInt(split.get(1), -1));
+ if (function == null) {
+ problemsHolder.registerProblem(warningHolder, "Undefined callback function '" + fullName + "'" + " (behaviour '" + entry.getValue() + "')");
+ }
+ }
+ }
+}
@@ -77,4 +77,7 @@
@NotNull
ArrayList<ErlangImportFunction> getImportedFunctions();
+
+ @NotNull
+ Collection<String> getAllCallbacksFullNames();
}
@@ -159,9 +159,19 @@ private Boolean calcExportAll() {
return myAttributeValue.getValue();
}
+ @NotNull
+ @Override
+ public Collection<String> getAllCallbacksFullNames() {
+ return getCallbackMap().keySet();
+ }
+
@Nullable
@Override
public ErlangCallbackSpec getCallbackByName(@NotNull String fullName) {
+ return getCallbackMap().get(fullName);
+ }
+
+ private Map<String, ErlangCallbackSpec> getCallbackMap() {
if (myCallbackMap == null) {
myCallbackMap = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider<Map<String, ErlangCallbackSpec>>() {
@Nullable
@@ -171,7 +181,7 @@ public ErlangCallbackSpec getCallbackByName(@NotNull String fullName) {
}
}, false);
}
- return myCallbackMap.getValue().get(fullName);
+ return myCallbackMap.getValue();
}
private Map<String, ErlangCallbackSpec> calcCallbacks() {
@@ -0,0 +1,2 @@
+-module(b1).
+-callback init(ok) -> ok.
@@ -0,0 +1,2 @@
+-module(b2).
+-callback init(ok) -> ok.
@@ -0,0 +1,4 @@
+<warning>-module(test).</warning>
+
+-behaviour(b1).
+-behaviour(b2).
@@ -23,6 +23,7 @@
import org.intellij.erlang.folding.ErlangFoldingBuilderTest;
import org.intellij.erlang.formatting.ErlangAutoIndentTest;
import org.intellij.erlang.formatting.ErlangFormattingTest;
+import org.intellij.erlang.highlighting.ErlangBehaviourInspectionsTest;
import org.intellij.erlang.highlighting.ErlangHighlightingTest;
import org.intellij.erlang.info.ErlangParameterInfoHandlerTest;
import org.intellij.erlang.parser.ErlangAppParserTest;
@@ -58,6 +59,7 @@ public static TestSuite suite() {
suite.addTestSuite(ErlangHeadMismatchFixTest.class);
suite.addTestSuite(ErlangTypedHandlerTest.class);
suite.addTestSuite(ErlangSdkReleaseTest.class);
+ suite.addTestSuite(ErlangBehaviourInspectionsTest.class);
return suite;
}
}
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2013 Sergey Ignatov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.intellij.erlang.highlighting;
+
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
+import org.intellij.erlang.inspection.ErlangUndefinedCallbackFunctionInspection;
+
+/**
+ * @author ignatov
+ */
+public class ErlangBehaviourInspectionsTest extends LightPlatformCodeInsightFixtureTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ //noinspection unchecked
+ myFixture.enableInspections(ErlangUndefinedCallbackFunctionInspection.class);
+ }
+
+ @Override
+ protected String getTestDataPath() {
+ return "testData/highlighting/behaviour/";
+ }
+
+ public void testSimple() throws Exception {
+ myFixture.configureByFiles("b1.erl", "b2.erl", "test.erl");
+ myFixture.checkHighlighting(true, false, false);
+ }
+
+ @Override
+ protected boolean isWriteActionRequired() {
+ return false;
+ }
+}

0 comments on commit 8d5fabb

Please sign in to comment.