Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 68f44b1513b01a6969b1d1f000eeade3109fce90 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*******************************************************************************
 * Copyright (c) 2009, 2012 Alena Laskavaia
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Alena Laskavaia  - initial API and implementation
 *     Patrick Hofer [bug 315528]
 *     Sergey Prigogin (Google)
 *     Marc-Andre Laperle
 *******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers;

import java.util.HashSet;

import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;

/**
 * Checker to find that class has virtual method and non virtual destructor
 *
 * @author Alena Laskavaia
 */
public class NonVirtualDestructor extends AbstractIndexAstChecker {
	public static final String PROBLEM_ID = "org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem"; //$NON-NLS-1$

	// Prevent stack overflow in case: class A: public A {};
	private static HashSet<ICPPClassType> checkedClassTypes = new HashSet<>();

	@Override
	public void processAst(IASTTranslationUnit ast) {
		// Traverse the AST using the visitor pattern.
		ast.accept(new OnEachClass());
	}

	private static ICPPMethod getDestructor(ICPPClassType classType) {
		for (ICPPMethod method : classType.getDeclaredMethods()) {
			if (method.isDestructor()) {
				return method;
			}
		}
		return null;
	}

	private static boolean hasVirtualDestructor(ICPPClassType classType) {
		checkedClassTypes.add(classType);
		ICPPMethod destructor = getDestructor(classType);
		if (destructor != null && destructor.isVirtual()) {
			return true;
		}
		ICPPBase[] bases = classType.getBases();
		for (ICPPBase base : bases) {
			IBinding baseClass = base.getBaseClass();
			if (baseClass instanceof ICPPClassType) {
				ICPPClassType cppClassType = (ICPPClassType) baseClass;
				if (!checkedClassTypes.contains(cppClassType) && hasVirtualDestructor(cppClassType)) {
					return true;
				}
			}
		}
		return false;
	}

	private class OnEachClass extends ASTVisitor {
		OnEachClass() {
			shouldVisitDeclSpecifiers = true;
		}

		@Override
		public int visit(IASTDeclSpecifier decl) {
			if (decl instanceof ICPPASTCompositeTypeSpecifier) {
				ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) decl;
				IASTName className = spec.getName();
				IBinding binding = className.resolveBinding();
				if (!(binding instanceof ICPPClassType)) {
					return PROCESS_SKIP;
				}
				try {
					CPPSemantics.pushLookupPoint(className);
					ICPPClassType classType = (ICPPClassType) binding;
					boolean hasVirtualDestructor = hasVirtualDestructor(classType);
					checkedClassTypes.clear();
					if (hasVirtualDestructor) {
						return PROCESS_SKIP;
					}
					ICPPMethod virtualMethod = null;
					for (ICPPMethod method : ClassTypeHelper.getAllDeclaredMethods(classType)) {
						if (!method.isDestructor() && method.isVirtual()) {
							virtualMethod = method;
						}
					}
					if (virtualMethod == null) {
						return PROCESS_SKIP;
					}
					ICPPMethod destructor = getDestructor(classType);
					if (destructor != null && destructor.getVisibility() != ICPPASTVisibilityLabel.v_public
							&& classType.getFriends().length == 0) {
						// No error if the destructor is protected or private and there are no friends.
						return PROCESS_SKIP;
					}

					IASTNode node = decl;
					if (destructor instanceof ICPPInternalBinding) {
						IASTNode[] decls = ((ICPPInternalBinding) destructor).getDeclarations();
						if (decls != null && decls.length > 0) {
							node = decls[0];
						}
					}
					reportProblem(PROBLEM_ID, node, new String(className.getSimpleID()), virtualMethod.getName());
					return PROCESS_SKIP;
				} finally {
					CPPSemantics.popLookupPoint();
				}
			}
			return PROCESS_CONTINUE;
		}
	}
}

Back to the top