Improved Remove Unused Declarations refactoring.

Change-Id: I540ef470eed2355eedd76e592d893501a30fc4c9
diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java
index d212dd4..e3d768e 100644
--- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java
+++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/reducer/RemoveUnusedDeclarationsRefactoring.java
@@ -61,9 +61,11 @@
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
@@ -74,6 +76,7 @@
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
 import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPPartialSpecialization;
 import org.eclipse.cdt.core.formatter.DefaultCodeFormatterOptions;
 import org.eclipse.cdt.core.index.IIndex;
 import org.eclipse.cdt.core.model.ICElement;
@@ -81,6 +84,7 @@
 import org.eclipse.cdt.core.parser.util.CharArrayUtils;
 import org.eclipse.cdt.ui.refactoring.CTextFileChange;
 
+import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
 import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
 import org.eclipse.cdt.internal.core.dom.rewrite.util.ASTNodes;
@@ -262,6 +266,9 @@
 			return false;
 		}
 
+		if (ASTQueries.findAncestorWithType(declaration, ICPPASTLambdaExpression.class) != null)
+			return true; // Removing inside lambda expressions is unsafe.
+
 		Collection<IASTName> declaredNames = getDeclaredNames(declaration);
 		if (declaredNames == null)
 			return true;
@@ -290,7 +297,7 @@
 						IASTNode[] declarations = ((ICPPInternalBinding) binding).getDeclarations();
 						if (declarations != null && declarations.length != 0) {
 							IASTNode firstDeclaration = declarations[0];
-							int firstDeclarationOffset = ASTNodes.offset(firstDeclaration);
+							int firstDeclarationOffset = ASTNodes.endOffset(firstDeclaration);
 							if (startOffset > firstDeclarationOffset) {
 								startPoint = firstDeclaration;
 								startOffset = firstDeclarationOffset;
@@ -303,11 +310,17 @@
 			for (IASTName name : names) {
 				if (name != declName) {
 					char[] nameChars = name.getSimpleID();
+
 					int offset = nameChars.length != 0 && nameChars[0] == '~' ? 1 : 0;
-					if (CharArrayUtils.equals(nameChars, offset, nameChars.length - offset, declNameChars)
-							&& (ASTNodes.offset(name) >= startOffset
-									|| isInsideTemplateDeclarationOrSpecialization(name))) {
-						return true;
+					if (CharArrayUtils.equals(nameChars, offset, nameChars.length - offset, declNameChars)) {
+						IASTDeclaration decl = findTopmostNonTemplateDeclaration(name);
+						if (decl == null) {
+							if (ASTNodes.offset(name) >= startOffset)
+								return true;
+						} else {
+							if (!isDeclaredBy(name, decl))
+								return true;
+						}
 					}
 				}
 			}
@@ -364,10 +377,63 @@
 		return null;
 	}
 
-	private static boolean isInsideTemplateDeclarationOrSpecialization(IASTNode node) {
-		while ((node = node.getParent()) != null) {
-			if (node instanceof ICPPASTTemplateDeclaration || node instanceof ICPPASTTemplateSpecialization)
-				return true;
+	/**
+	 * Returns the topmost non-template declaration declaring the given name, or {@code null}
+	 * if the name appears in a non-declaration context.
+	 */
+	private static IASTDeclaration findTopmostNonTemplateDeclaration(IASTName name) {
+		IASTNode node = name;
+		ASTNodeProperty property = name.getPropertyInParent();
+		while (property == ICPPASTTemplateId.TEMPLATE_NAME
+				|| property == ICPPASTQualifiedName.SEGMENT_NAME && node == ((ICPPASTQualifiedName) node.getParent()).getLastName()) {
+			node = node.getParent();
+			property = node.getPropertyInParent();
+		}
+		if (property == IASTDeclarator.DECLARATOR_NAME || property == IASTCompositeTypeSpecifier.TYPE_NAME) {
+			node = node.getParent().getParent();
+		}
+		for (IASTNode parent; (parent = node.getParent()) != null; node = parent) {
+			if (!(parent instanceof IASTDeclaration)
+					|| parent instanceof ICPPASTTemplateDeclaration
+					|| parent instanceof ICPPASTTemplateSpecialization) {
+				if (node instanceof IASTDeclaration) {
+					return (IASTDeclaration) node;
+				}
+				break;
+			}
+		}
+			
+		return null;
+	}
+
+	/**
+	 * Checks if {@code name} is declared by the {@code declaration}.
+	 */
+	private static boolean isDeclaredBy(IASTName name, IASTDeclaration declaration) {
+		if (declaration.getPropertyInParent() == ICPPASTTemplateSpecialization.OWNED_DECLARATION)
+			return false;
+		if (name.getPropertyInParent() == ICPPASTTemplateId.TEMPLATE_NAME && ((ICPPASTTemplateId) name.getParent()).resolveBinding() instanceof ICPPPartialSpecialization)
+			return false;
+		if (declaration instanceof IASTSimpleDeclaration) {
+			IASTDeclSpecifier declSpec = ((IASTSimpleDeclaration) declaration).getDeclSpecifier();
+			if (declSpec instanceof ICPPASTDeclSpecifier && ((ICPPASTDeclSpecifier) declSpec).isFriend())
+				return false;
+		}
+
+		while (name instanceof ICPPASTTemplateId) {
+			name = ((ICPPASTTemplateId) name).getTemplateName(); 
+		}
+		char[] nameChars = name.getSimpleID();
+		Collection<IASTName> declaredNames = getDeclaredNames(declaration);
+		if (declaredNames != null) {
+			for (IASTName declaredName : declaredNames) {
+				while (declaredName instanceof ICPPASTTemplateId) {
+					declaredName = ((ICPPASTTemplateId) declaredName).getTemplateName(); 
+				}
+				char[] declaredNameChars = declaredName.getSimpleID();
+				if (Arrays.equals(nameChars, declaredNameChars))
+					return true;
+			}
 		}
 			
 		return false;