forked from include-what-you-use/include-what-you-use
-
Notifications
You must be signed in to change notification settings - Fork 6
/
iwyu_location_util.cc
188 lines (169 loc) · 7.56 KB
/
iwyu_location_util.cc
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//===--- iwyu_location_util.cc - SourceLoc-related utilities for iwyu -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "iwyu_location_util.h"
#include "iwyu_ast_util.h"
#include "iwyu_port.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
using clang::BinaryOperator;
using clang::CXXDependentScopeMemberExpr;
using clang::CXXMethodDecl;
using clang::CXXOperatorCallExpr;
using clang::ClassTemplateSpecializationDecl;
using clang::ConditionalOperator;
using clang::FileEntry;
using clang::FunctionDecl;
using clang::MemberExpr;
using clang::SourceLocation;
using clang::UnaryOperator;
using clang::UnresolvedMemberExpr;
namespace include_what_you_use {
// This works around two bugs(?) in clang where decl->getLocation()
// can be wrong for implicit template instantiations and functions.
// (1) Consider the following code:
// template<class T> hash { ... }; // tpl decl
// template<class T> hash<basic_string<T>> { ... }; // partial spec decl
// hash<basic_string<char>> myhash;
// The decl associated with hash<basic_string<char>> is a third decl
// that is formed implicitly from the partial-spec decl. The bug(?) is
// that clang gives the third decl the wrong location: it should have
// the location of the partial-spec decl it is instantiating, but
// instead it has the location of original tpl decl. (clang gets
// everything else right -- PrintableDecl(third_decl) shows the right
// class body -- but the location is wrong.) We work around that here
// by using GetInstantiatedFromDecl to map an implicit decl back to
// the appropriate decl that actually defines the class.
// (2) Consider this code:
// struct A { ... };
// struct A; // a little late, but a forward-declaration
// clang will associate the implicit constructors and destructor with
// the last declaration, which is the forward-declare, rather than the
// actual definition. Luckily, the implicit constructor's parent is
// still correct, so we just use that as the location. Implicit
// methods don't have their own location anyway.
// Note the two issues can both be present, if an implicit method's
// parent is an implicit instantiation.
SourceLocation GetLocation(const clang::Decl* decl) {
if (decl == nullptr)
return SourceLocation();
if (const CXXMethodDecl* method_decl = DynCastFrom(decl)) {
if (method_decl->isImplicit())
decl = method_decl->getParent();
}
if (const ClassTemplateSpecializationDecl* spec = DynCastFrom(decl)) {
decl = GetDefinitionAsWritten(spec); // templated class
} else if (const FunctionDecl* fn_decl = DynCastFrom(decl)) {
if (fn_decl->getTemplateInstantiationPattern()) // templated function
decl = GetDefinitionAsWritten(fn_decl);
}
return decl->getLocation();
}
// Unfortunately member_expr doesn't expose the location of the . or
// ->. If the base is implicit, there is no . or ->, and we just
// return the member loc. Otherwise, we have to guess if the entire
// member-expression (all of 'b.m') is in a macro or not. We look at
// getMemberLoc(), the start of the member ('m') , and
// getBase()->getEndLoc(), the end of the base ('b'). If they're both
// on the same line of the same file, then the . or -> must be there
// too, and return that as the location. Otherwise, we assume that
// one or the other is in a macro, but the . or -> is not, and use the
// instantiation (not spelling) location of the macro.
static SourceLocation GetMemberExprLocation(const MemberExpr* member_expr) {
const SourceLocation member_start = member_expr->getMemberLoc();
const SourceLocation base_end = member_expr->getBase()->getEndLoc();
if (member_expr->isImplicitAccess() || base_end.isInvalid())
return member_start;
// Weird: member_start can be 'invalid' for calls like bool(x),
// where bool() is a class's own operator bool. Shrug.
if (member_start.isInvalid())
return base_end;
// If either the base or the member is not a macro, then we consider
// the location of this member-expr to be outside the macro.
if (!IsInMacro(member_start))
return member_start;
if (!IsInMacro(base_end))
return base_end;
// Now figure out if the base and member are in the same macro. If
// so, we say the whole member-expr is part of that macro.
// Otherwise, we just say the member-expr is in the file where the
// member and base macros are called.
if (GetFileEntry(member_start) == GetFileEntry(base_end) &&
GetLineNumber(member_start) == GetLineNumber(base_end)) {
return member_start;
}
return GetInstantiationLoc(member_start);
}
SourceLocation GetLocation(const clang::Stmt* stmt) {
if (stmt == nullptr)
return SourceLocation();
// For some expressions, we take the location to be the 'key' part
// of the expression, not the beginning. For instance, the
// location of 'a << b' is the '<<', not the 'a'. This is
// important for code like 'MACRO << 5', where we want to make
// sure the location we return is "here", and not inside MACRO.
// (The price is we do worse for '#define OP <<; a OP b;'.)
if (const CXXOperatorCallExpr* call_expr = DynCastFrom(stmt)) {
return call_expr->getOperatorLoc();
} else if (const MemberExpr* member_expr = DynCastFrom(stmt)) {
return GetMemberExprLocation(member_expr);
} else if (const UnresolvedMemberExpr* member_expr = DynCastFrom(stmt)) {
if (member_expr->getOperatorLoc().isValid())
return member_expr->getOperatorLoc();
} else if (const CXXDependentScopeMemberExpr* member_expr =
DynCastFrom(stmt)) {
if (member_expr->getOperatorLoc().isValid())
return member_expr->getOperatorLoc();
} else if (const BinaryOperator* binary_op = DynCastFrom(stmt)) {
return binary_op->getOperatorLoc();
} else if (const ConditionalOperator* conditional_op = DynCastFrom(stmt)) {
return conditional_op->getQuestionLoc();
} else if (const UnaryOperator* unary_op = DynCastFrom(stmt)) {
// Drill through unary operators and parentheses, to get at the underlying
// DeclRefExpr or whatever, e.g. '*(x)' should give the location of 'x'
stmt = unary_op->getSubExpr()->IgnoreParenImpCasts();
}
return stmt->getBeginLoc();
}
SourceLocation GetLocation(const clang::TypeLoc* typeloc) {
if (typeloc == nullptr)
return SourceLocation();
return typeloc->getBeginLoc();
}
SourceLocation GetLocation(const clang::NestedNameSpecifierLoc* nnsloc) {
if (nnsloc == nullptr)
return SourceLocation();
return nnsloc->getBeginLoc();
}
SourceLocation GetLocation(const clang::TemplateArgumentLoc* argloc) {
if (argloc == nullptr)
return SourceLocation();
return argloc->getLocation();
}
bool IsInScratchSpace(SourceLocation loc) {
return StartsWith(PrintableLoc(GetSpellingLoc(loc)), "<scratch space>");
}
bool IsInHeader(const clang::Decl* decl) {
const FileEntry* containing_file = GetFileEntry(decl);
if (!containing_file) {
// This is a builtin, or something is terribly wrong.
// At any rate, we're not in a header.
return false;
}
return !GlobalSourceManager()->isMainFile(*containing_file);
}
} // namespace include_what_you_use