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 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
//===--- SymbolOperation.cpp - --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/Refactor/SymbolOperation.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Tooling/Refactor/RefactoringActionFinder.h"
using namespace clang;
/// Return true if the given local record decl escapes the given enclosing
/// function or block \p Ctx.
static bool escapesEnclosingDecl(const DeclContext *Ctx, const RecordDecl *RD) {
QualType ReturnType;
bool DependentBlock = false;
if (const auto *FD = dyn_cast<FunctionDecl>(Ctx))
ReturnType = FD->getReturnType();
else if (const auto *BD = dyn_cast<BlockDecl>(Ctx)) {
ReturnType = BD->getSignatureAsWritten()->getType();
// Blocks that don't have an explicitly specified type (represented with a
// dependent type) could potentially return the record, e.g.
// auto block = ^ {
// struct Foo { };
// return Foo();
// };
if (const auto *FT = ReturnType->getAs<FunctionType>())
ReturnType = FT->getReturnType();
if (ReturnType->isDependentType())
DependentBlock = true;
} else
return false;
// The record can be returned from its enclosing function when the function's
// return type is auto.
//
// FIXME: Use a smarter heuristic that detects if the record type is
// actually returned from the function. Have to account for inner records,
// like in the example below:
//
// auto foo() {
// struct Foo { struct Bar { }; };
// return Foo::Bar();
// };
//
// for types that depend on the record, like in the example below:
//
// auto foo() {
// template<typename T> struct C<T> { T x; };
// struct Foo { struct Bar { }; };
// return C<Bar>();
// }
//
// and for things like typedefs and function types as well.
if (!DependentBlock && !ReturnType->getContainedAutoType())
return false;
// Even if the enclosing function returns the local record, this record is
// still local if the enclosing function is inside a function/method that
// doesn't return this record.
const auto *D = cast<Decl>(Ctx);
if (!D->isDefinedOutsideFunctionOrMethod())
return escapesEnclosingDecl(D->getParentFunctionOrMethod(), RD);
return true;
}
static bool escapesEnclosingDecl(const RecordDecl *RD,
const LangOptions &LangOpts) {
// We only care about things that escape in header files since things that
// escape in source files will be used only in the initial TU.
return LangOpts.IsHeaderFile &&
escapesEnclosingDecl(RD->getParentFunctionOrMethod(), RD);
}
static bool isInLocalScope(const Decl *D) {
const DeclContext *LDC = D->getLexicalDeclContext();
while (true) {
if (LDC->isFunctionOrMethod())
return true;
if (!isa<TagDecl>(LDC))
return false;
if (const auto *CRD = dyn_cast<CXXRecordDecl>(LDC))
if (CRD->isLambda())
return true;
LDC = LDC->getLexicalParent();
}
return false;
}
/// Return true if the given declaration corresponds to a local symbol.
bool clang::tooling::isLocalSymbol(const NamedDecl *FoundDecl,
const LangOptions &LangOpts) {
// Template parameters aren't indexed, so use local rename.
if (isa<TemplateTypeParmDecl>(FoundDecl) ||
isa<NonTypeTemplateParmDecl>(FoundDecl) ||
isa<TemplateTemplateParmDecl>(FoundDecl))
return true;
if (const auto *VD = dyn_cast<VarDecl>(FoundDecl))
return VD->isLocalVarDeclOrParm();
// Objective-C selector renames must be global.
if (isa<ObjCMethodDecl>(FoundDecl))
return false;
// Local declarations are defined in a function or a method, or are anonymous.
if (!isInLocalScope(FoundDecl))
return false;
// A locally defined record is global when it is returned from the enclosing
// function because we can refer to its destructor externally.
if (const auto *RD = dyn_cast<CXXRecordDecl>(FoundDecl))
return !escapesEnclosingDecl(RD, LangOpts);
// A locally defined field is global when its record is returned from the
// enclosing function.
if (const auto *FD = dyn_cast<FieldDecl>(FoundDecl))
return !escapesEnclosingDecl(FD->getParent(), LangOpts);
if (const auto *MD = dyn_cast<CXXMethodDecl>(FoundDecl)) {
// A locally defined method is global when its record is returned from the
// enclosing function.
if (escapesEnclosingDecl(MD->getParent(), LangOpts))
return false;
// Method renames can be local only iff this method doesn't override
// a global method, for example:
//
// void func() {
// struct Foo: GlobalSuper {
// // When renaming foo we should also rename GlobalSuper's foo
// void foo() override;
// }
// }
//
// FIXME: We can try to be smarter about it and check if we override
// a local method, which would make this method local as well.
return !MD->isVirtual();
}
return true;
}
static const NamedDecl *
findDeclThatRequiresImplementationTU(const NamedDecl *FoundDecl) {
// TODO: implement the rest.
if (const ObjCIvarDecl *IVarDecl = dyn_cast<ObjCIvarDecl>(FoundDecl)) {
// We need the implementation TU when the IVAR is declared in an @interface
// without an @implementation.
if (const auto *ID =
dyn_cast<ObjCInterfaceDecl>(IVarDecl->getDeclContext())) {
if (!ID->getImplementation())
return IVarDecl;
}
}
return nullptr;
}
namespace clang {
namespace tooling {
SymbolOperation::SymbolOperation(const NamedDecl *FoundDecl,
ASTContext &Context)
: IsLocal(isLocalSymbol(FoundDecl, Context.getLangOpts())) {
// Take the category declaration if this is a category implementation.
if (const auto *CategoryImplDecl =
dyn_cast<ObjCCategoryImplDecl>(FoundDecl)) {
if (const auto *CategoryDecl = CategoryImplDecl->getCategoryDecl())
FoundDecl = CategoryDecl;
}
// Use the property if this method is a getter/setter.
else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) {
if (const auto *PropertyDecl =
MethodDecl->getCanonicalDecl()->findPropertyDecl()) {
// Don't use the property if the getter/setter method has an explicitly
// specified name.
if (MethodDecl->param_size() == 0
? !PropertyDecl->hasExplicitGetterName()
: !PropertyDecl->hasExplicitSetterName())
FoundDecl = PropertyDecl;
}
}
DeclThatRequiresImplementationTU =
findDeclThatRequiresImplementationTU(FoundDecl);
// TODO: Split into initiation that works after implementation TU is loaded.
// Find the set of symbols that this operation has to work on.
auto AddSymbol = [this, &Context](const NamedDecl *FoundDecl) {
unsigned Index = Symbols.size();
Symbols.push_back(rename::Symbol(FoundDecl, Index, Context.getLangOpts()));
for (const auto &USR : findSymbolsUSRSet(FoundDecl, Context))
USRToSymbol.insert(std::make_pair(USR.getKey(), Index));
};
AddSymbol(FoundDecl);
// Take getters, setters and ivars into account when dealing with
// Objective-C @property declarations.
if (const auto *PropertyDecl = dyn_cast<ObjCPropertyDecl>(FoundDecl)) {
// FIXME: findSymbolsUSRSet is called for every symbol we add, which is
// inefficient since we currently have to traverse the AST every time it is
// called. Fix this so that the AST isn't traversed more than once.
if (!PropertyDecl->hasExplicitGetterName()) {
if (const auto *Getter = PropertyDecl->getGetterMethodDecl())
AddSymbol(Getter);
}
if (!PropertyDecl->hasExplicitSetterName()) {
if (const auto *Setter = PropertyDecl->getSetterMethodDecl())
AddSymbol(Setter);
}
}
}
} // end namespace tooling
} // end namespace clang
|