blob: 356c38ad283b59c4f8a5f071257bbd0483432c66 [file] [log] [blame]
/*
* Copyright 2016 The Kythe Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "kythe/cxx/indexer/cxx/clang_utils.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Lex/Lexer.h"
#include "glog/logging.h"
namespace kythe {
bool isObjCSelector(const clang::DeclarationName& DN) {
switch (DN.getNameKind()) {
case clang::DeclarationName::NameKind::ObjCZeroArgSelector:
case clang::DeclarationName::NameKind::ObjCOneArgSelector:
case clang::DeclarationName::NameKind::ObjCMultiArgSelector:
return true;
default:
return false;
}
}
void SkipWhitespace(const clang::SourceManager& source_manager,
clang::SourceLocation* loc) {
CHECK(loc != nullptr);
while (clang::isWhitespace(*source_manager.getCharacterData(*loc))) {
*loc = loc->getLocWithOffset(1);
}
}
clang::SourceRange RangeForASTEntityFromSourceLocation(
const clang::SourceManager& source_manager,
const clang::LangOptions& lang_options,
clang::SourceLocation start_location) {
if (start_location.isFileID()) {
clang::SourceRange first_token_range =
RangeForSingleTokenFromSourceLocation(source_manager, lang_options,
start_location);
llvm::SmallVector<char, 32> buffer;
// TODO(jdennett): Prefer to lex a token and then check if it is
// `clang::tok::kw_operator`, instead of checking the spelling?
// If we do that, update `RangeForOperatorName` to take a `clang::Token`
// as its argument?
llvm::StringRef token_spelling = clang::Lexer::getSpelling(
start_location, buffer, source_manager, lang_options);
if (token_spelling == "operator") {
return RangeForOperatorName(source_manager, lang_options,
first_token_range);
} else {
return first_token_range;
}
} else {
// The location is from macro expansion. We always need to return a
// location that can be associated with the original file.
clang::SourceLocation file_loc = source_manager.getFileLoc(start_location);
if (IsTopLevelNonMacroMacroArgument(source_manager, start_location)) {
// TODO(jdennett): Test cases such as `MACRO(operator[])`, where the
// name in the macro argument should ideally not just be linked to a
// single token.
return RangeForSingleTokenFromSourceLocation(source_manager, lang_options,
file_loc);
} else {
// The entity is in a macro expansion, and it is not a top-level macro
// argument that itself is not expanded from a macro. The range
// is a 0-width span (a point), so no source link will be created.
return clang::SourceRange(file_loc);
}
}
}
clang::SourceRange RangeForOperatorName(
const clang::SourceManager& source_manager,
const clang::LangOptions& lang_options,
const clang::SourceRange& operator_token_range) {
// Make a longer range than `operator_token_range`, if possible, so that
// the range captures which operator this is. There are two kinds of
// operators; type conversion operators, and overloaded operators.
// Most of the overloaded operators have names `operator ???` for some
// token `???`, but there are exceptions (namely: `operator[]`,
// `operator()`, `operator new[]` and `operator delete[]`.
//
// If this is a conversion operator to some type T, we link from the
// `operator` keyword only, but if it's an overloaded operator then
// we'd like to link from the name, e.g., `operator[]`, so long as
// that is spelled out in the source file.
clang::SourceLocation Pos = operator_token_range.getEnd();
SkipWhitespace(source_manager, &Pos);
// TODO(jdennett): Find a better name for `Token2EndLocation`, to
// indicate that it's the end of the first token after `operator`.
clang::SourceLocation Token2EndLocation =
GetLocForEndOfToken(source_manager, lang_options, Pos);
if (!Token2EndLocation.isValid()) {
// Well, this is the best we can do then.
return operator_token_range;
}
// TODO(jdennett): Prefer checking the token's type here also, rather
// than the spelling.
llvm::SmallVector<char, 32> Buffer;
const clang::StringRef Token2Spelling =
clang::Lexer::getSpelling(Pos, Buffer, source_manager, lang_options);
// TODO(jdennett): Handle (and test) operator new, operator new[],
// operator delete, and operator delete[].
if (!Token2Spelling.empty() &&
(Token2Spelling == "::" ||
clang::Lexer::isIdentifierBodyChar(Token2Spelling[0], lang_options))) {
// The token after `operator` is an identifier or keyword, or the
// scope resolution operator `::`, so just return the range of
// `operator` -- this is presumably a type conversion operator, and
// the type-visiting code will add any appropriate links from the
// type, so we shouldn't link from it here.
return operator_token_range;
}
if (Token2Spelling == "(" || Token2Spelling == "[") {
// TODO(jdennett): Do better than this disgusting hack to skip
// whitespace. We probably want to actually instantiate a lexer.
clang::SourceLocation Pos = Token2EndLocation;
SkipWhitespace(source_manager, &Pos);
clang::Token Token3;
if (clang::Lexer::getRawToken(Pos, Token3, source_manager, lang_options)) {
// That's weird, we've got just part of the operator name -- we saw
// an opening "(" or "]", but not its closing partner. The best
// we can do is to just return the range covering `operator`.
llvm::errs() << "Failed to lex token after " << Token2Spelling.str();
return operator_token_range;
}
if (Token3.is(clang::tok::r_square) || Token3.is(clang::tok::r_paren)) {
clang::SourceLocation EndLocation = GetLocForEndOfToken(
source_manager, lang_options, Token3.getLocation());
return clang::SourceRange(operator_token_range.getBegin(), EndLocation);
}
return operator_token_range;
}
// In this case we assume we have `operator???`, for some single-token
// operator name `???`.
return clang::SourceRange(operator_token_range.getBegin(), Token2EndLocation);
}
clang::SourceLocation GetLocForEndOfToken(
const clang::SourceManager& source_manager,
const clang::LangOptions& lang_options,
clang::SourceLocation start_location) {
if (start_location.isMacroID()) {
start_location = source_manager.getExpansionLoc(start_location);
}
return clang::Lexer::getLocForEndOfToken(start_location,
0 /* offset from end of token */,
source_manager, lang_options);
}
clang::SourceRange RangeForSingleTokenFromSourceLocation(
const clang::SourceManager& source_manager,
const clang::LangOptions& lang_options, clang::SourceLocation start) {
CHECK(start.isFileID());
clang::SourceLocation end =
GetLocForEndOfToken(source_manager, lang_options, start);
return clang::SourceRange(start, end);
}
bool IsTopLevelNonMacroMacroArgument(
const clang::SourceManager& source_manager,
const clang::SourceLocation& source_location) {
if (!source_location.isMacroID()) return false;
clang::SourceLocation loc = source_location;
// We want to get closer towards the initial macro typed into the source only
// if the location is being expanded as a macro argument.
while (source_manager.isMacroArgExpansion(loc)) {
// We are calling getImmediateMacroCallerLoc, but note it is essentially
// equivalent to calling getImmediateSpellingLoc in this context according
// to Clang implementation. We are not calling getImmediateSpellingLoc
// because Clang comment says it "should not generally be used by clients."
loc = source_manager.getImmediateMacroCallerLoc(loc);
}
return !loc.isMacroID();
}
const clang::Decl* FindSpecializedTemplate(const clang::Decl* decl) {
if (const auto* FD = llvm::dyn_cast<const clang::FunctionDecl>(decl)) {
if (auto* ftsi = FD->getTemplateSpecializationInfo()) {
if (!ftsi->isExplicitInstantiationOrSpecialization()) {
return ftsi->getTemplate();
}
}
} else if (const auto* ctsd =
llvm::dyn_cast<const clang::ClassTemplateSpecializationDecl>(
decl)) {
if (!ctsd->isExplicitInstantiationOrSpecialization()) {
auto primary_or_partial = ctsd->getSpecializedTemplateOrPartial();
if (const auto* partial =
primary_or_partial
.dyn_cast<clang::ClassTemplatePartialSpecializationDecl*>()) {
return partial;
} else if (const auto* primary =
primary_or_partial.dyn_cast<clang::ClassTemplateDecl*>()) {
return primary;
}
}
} else if (const auto* vtsd =
llvm::dyn_cast<const clang::VarTemplateSpecializationDecl>(
decl)) {
if (!vtsd->isExplicitInstantiationOrSpecialization()) {
auto primary_or_partial = vtsd->getSpecializedTemplateOrPartial();
if (const auto* partial =
primary_or_partial
.dyn_cast<clang::VarTemplatePartialSpecializationDecl*>()) {
return partial;
} else if (const auto* primary =
primary_or_partial.dyn_cast<clang::VarTemplateDecl*>()) {
return primary;
}
}
}
return decl;
}
} // namespace kythe