Bug 389577 - False positive "Unused static function"

Add handling of constructor/destructor attributes and some improvement
when functions were previously declared but not defined as used.

Change-Id: I7537bc87c6c4bc5b294d8e15fe5b42c92b3f2974
Signed-off-by: Marc-Andre Laperle <malaperle@gmail.com>
diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java
index ebb2fca..607a1e6 100644
--- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java
+++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/UnusedSymbolInFileScopeChecker.java
@@ -13,10 +13,12 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import org.eclipse.cdt.codan.checkers.CodanCheckersActivator;
 import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
@@ -26,6 +28,7 @@
 import org.eclipse.cdt.codan.core.param.ListProblemPreference;
 import org.eclipse.cdt.core.dom.ast.ASTVisitor;
 import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
+import org.eclipse.cdt.core.dom.ast.IASTAttributeOwner;
 import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
 import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
 import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
@@ -62,13 +65,17 @@
 	public static final String ER_UNUSED_STATIC_FUNCTION_ID = "org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem"; //$NON-NLS-1$
 	public static final String PARAM_MACRO_ID = "macro"; //$NON-NLS-1$
 	public static final String PARAM_EXCEPT_ARG_LIST = "exceptions"; //$NON-NLS-1$
-	private static final String[] ATTRIBUTE_UNUSED = new String[] { "__unused__", "unused" };  //$NON-NLS-1$//$NON-NLS-2$
+	/*
+	 * Various attributes that when present for a symbol should make it considered as used.
+	 */
+	private static final String[] USAGE_ATTRIBUTES = new String[] { "__unused__", "unused", "constructor", "destructor" };  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
 	private Map<IBinding, IASTDeclarator> externFunctionDeclarations = new HashMap<IBinding, IASTDeclarator>();
 	private Map<IBinding, IASTDeclarator> staticFunctionDeclarations = new HashMap<IBinding, IASTDeclarator>();
 	private Map<IBinding, IASTDeclarator> staticFunctionDefinitions = new HashMap<IBinding, IASTDeclarator>();
 	private Map<IBinding, IASTDeclarator> externVariableDeclarations = new HashMap<IBinding, IASTDeclarator>();
 	private Map<IBinding, IASTDeclarator> staticVariableDeclarations = new HashMap<IBinding, IASTDeclarator>();
+	private Set<IBinding> declarationsWithUsageAttributes = new HashSet<>();
 	private IProblemWorkingCopy unusedVariableProblem = null;
 
 	@Override
@@ -118,6 +125,7 @@
 			filterOutUsedElements(ast);
 			reportProblems();
 		}
+		declarationsWithUsageAttributes.clear();
 	}
 
 	private void collectCandidates(IASTTranslationUnit ast) {
@@ -134,16 +142,15 @@
 						IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) element;
 
 						IASTDeclSpecifier declSpec = simpleDeclaration.getDeclSpecifier();
-						if (AttributeUtil.hasAttribute(declSpec, ATTRIBUTE_UNUSED)) {
-							return PROCESS_SKIP;
-						}
+						boolean hasUsageAttrib = hasUsageAttribute(declSpec);
 						IASTDeclarator[] declarators = simpleDeclaration.getDeclarators();
 						for (IASTDeclarator decl : declarators) {
-							if (AttributeUtil.hasAttribute(decl, ATTRIBUTE_UNUSED))
-								continue;
 							IASTName astName = decl.getName();
 							if (astName != null) {
 								IBinding binding = astName.resolveBinding();
+								if (hasUsageAttrib || hasUsageAttribute(decl)) {
+									declarationsWithUsageAttributes.add(binding);
+								}
 								int storageClass = simpleDeclaration.getDeclSpecifier().getStorageClass();
 
 								if (binding instanceof IFunction) {
@@ -206,10 +213,12 @@
 						IASTName astName = declarator.getName();
 						if (astName != null) {
 							IBinding binding = astName.resolveBinding();
+							if (hasUsageAttribute(definition.getDeclSpecifier())) {
+								declarationsWithUsageAttributes.add(binding);
+							}
 
 							if (definition.getDeclSpecifier().getStorageClass() == IASTDeclSpecifier.sc_static &&
-									!(astName instanceof ICPPASTQualifiedName) &&
-									!AttributeUtil.hasAttribute(declarator, ATTRIBUTE_UNUSED)) {
+									!(astName instanceof ICPPASTQualifiedName)) {
 								staticFunctionDefinitions.put(binding, declarator);
 							}
 
@@ -222,6 +231,9 @@
 					return PROCESS_SKIP;
 				}
 
+				private boolean hasUsageAttribute(IASTAttributeOwner attributeOwner) {
+					return AttributeUtil.hasAttribute(attributeOwner, USAGE_ATTRIBUTES);
+				}
 			});
 		} catch (Exception e) {
 			CodanCheckersActivator.log(e);
@@ -283,6 +295,14 @@
 						staticVariableDeclarations.remove(binding);
 					}
 
+					if (declarationsWithUsageAttributes.contains(binding)) {
+						staticFunctionDeclarations.remove(binding);
+						externFunctionDeclarations.remove(binding);
+						staticFunctionDefinitions.remove(binding);
+						externVariableDeclarations.remove(binding);
+						staticVariableDeclarations.remove(binding);
+					}
+
 					if (!isAnyCandidate())
 						return PROCESS_ABORT;
 
diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/UnusedSymbolInFileScopeCheckerTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/UnusedSymbolInFileScopeCheckerTest.java
index 693c064..270a66a 100644
--- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/UnusedSymbolInFileScopeCheckerTest.java
+++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/UnusedSymbolInFileScopeCheckerTest.java
@@ -315,9 +315,16 @@
 	// int f1() __attribute__((__unused__));
 	// extern int f2() __attribute__((unused));
 	// static void f3() __attribute__((unused));
+	// void f3() {}
 	// static void f4() __attribute__((unused));
 	// static void f4() __attribute__((unused)) {}
 	// static void __attribute__((unused)) f5();
+	// static void f6() __attribute__((unused));
+	// static void f6() {}
+	// static void __attribute__((unused)) f7();
+	// void f7() {}
+	// static void __attribute__((unused)) f8();
+	// static void f8() {}
 	public void testAttributeUnused() throws Exception {
 		loadCodeAndRunC(getAboveComment());
 		checkNoErrors();
@@ -326,6 +333,18 @@
 		checkNoErrors();
 	}
 
+	// static void __attribute__((constructor)) Ctor() {}
+	// static void __attribute__((destructor)) Dtor();
+	// static void Dtor2() __attribute__((destructor));
+	// static void Dtor3() __attribute__((destructor));
+	// static void Dtor() {}
+	// static void Dtor2() {}
+	// void Dtor3() {}
+	public void testAttributeConstructorDestructor_bug389577() throws Exception {
+		loadCodeAndRun(getAboveComment());
+		checkNoErrors();
+	}
+
 	//	extern int* pxCurrentTCB;
 	//
 	//	int main() {