Skip to content

🍒 [6.2] [SourceKit] Support location info for macro-expanded Clang imports #82171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/swift/AST/ASTNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerUnion.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/SourceManager.h"
#include "swift/AST/TypeAlignments.h"

namespace llvm {
Expand Down Expand Up @@ -98,6 +99,12 @@ namespace swift {
return llvm::hash_value(N.getOpaqueValue());
}
};

/// Find the outermost range that \p range was originally generated from.
/// Returns an invalid source range if \p range wasn't generated from a macro.
SourceRange getUnexpandedMacroRange(const SourceManager &SM,
SourceRange range);

} // namespace swift

namespace llvm {
Expand Down
13 changes: 0 additions & 13 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,19 +434,6 @@ class ModuleDecl
/// \c nullptr if the source location isn't in this module.
SourceFile *getSourceFileContainingLocation(SourceLoc loc);

// Retrieve the buffer ID and source range of the outermost node that
// caused the generation of the buffer containing \p range. \p range and its
// buffer if it isn't in a generated buffer or has no original range.
std::pair<unsigned, SourceRange> getOriginalRange(SourceRange range) const;

// Retrieve the buffer ID and source location of the outermost location that
// caused the generation of the buffer containing \p loc. \p loc and its
// buffer if it isn't in a generated buffer or has no original location.
std::pair<unsigned, SourceLoc> getOriginalLocation(SourceLoc loc) const {
auto [buffer, range] = getOriginalRange(loc);
return std::make_pair(buffer, range.Start);
}

/// Creates a map from \c #filePath strings to corresponding \c #fileID
/// strings, diagnosing any conflicts.
///
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef SWIFT_BASIC_SOURCEMANAGER_H
#define SWIFT_BASIC_SOURCEMANAGER_H

#include "swift/AST/ClangNode.h"
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/SourceLoc.h"
#include "clang/Basic/FileManager.h"
Expand All @@ -22,6 +23,7 @@
#include "llvm/Support/SourceMgr.h"
#include <map>
#include <optional>
#include <utility>
#include <vector>

namespace swift {
Expand Down Expand Up @@ -122,6 +124,10 @@ class GeneratedSourceInfo {
/// Contains the ancestors of this source buffer, starting with the root source
/// buffer and ending at this source buffer.
mutable llvm::ArrayRef<unsigned> ancestors = llvm::ArrayRef<unsigned>();

/// Clang node where this buffer comes from. This should be set when this is
/// an 'AttributeFromClang'.
ClangNode clangNode = ClangNode();
};

/// This class manages and owns source buffers.
Expand Down
26 changes: 26 additions & 0 deletions lib/AST/ASTNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,29 @@ FUNC(Expr)
FUNC(Decl)
FUNC(Pattern)
#undef FUNC

SourceRange swift::getUnexpandedMacroRange(const SourceManager &SM,
SourceRange range) {
unsigned bufferID = SM.findBufferContainingLoc(range.Start);
SourceRange outerRange;
while (const auto *info = SM.getGeneratedSourceInfo(bufferID)) {
switch (info->kind) {
#define MACRO_ROLE(Name, Description) \
case GeneratedSourceInfo::Name##MacroExpansion:
#include "swift/Basic/MacroRoles.def"
if (auto *customAttr = info->attachedMacroCustomAttr)
outerRange = customAttr->getRange();
else
outerRange =
ASTNode::getFromOpaqueValue(info->astNode).getSourceRange();
bufferID = SM.findBufferContainingLoc(outerRange.Start);
continue;
case GeneratedSourceInfo::ReplacedFunctionBody:
case GeneratedSourceInfo::PrettyPrinted:
case GeneratedSourceInfo::DefaultArgument:
case GeneratedSourceInfo::AttributeFromClang:
return SourceRange();
}
}
return outerRange;
}
54 changes: 6 additions & 48 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>

using namespace swift;

Expand Down Expand Up @@ -847,49 +848,6 @@ SourceFile *ModuleDecl::getSourceFileContainingLocation(SourceLoc loc) {
return nullptr;
}

std::pair<unsigned, SourceRange>
ModuleDecl::getOriginalRange(SourceRange range) const {
assert(range.isValid());

SourceManager &SM = getASTContext().SourceMgr;
unsigned bufferID = SM.findBufferContainingLoc(range.Start);

auto startRange = range;
unsigned startBufferID = bufferID;
while (const GeneratedSourceInfo *info =
SM.getGeneratedSourceInfo(bufferID)) {
switch (info->kind) {
#define MACRO_ROLE(Name, Description) \
case GeneratedSourceInfo::Name##MacroExpansion:
#include "swift/Basic/MacroRoles.def"
{
// Location was within a macro expansion, return the expansion site, not
// the insertion location.
if (info->attachedMacroCustomAttr) {
range = info->attachedMacroCustomAttr->getRange();
} else {
ASTNode expansionNode = ASTNode::getFromOpaqueValue(info->astNode);
range = expansionNode.getSourceRange();
}
bufferID = SM.findBufferContainingLoc(range.Start);
break;
}
case GeneratedSourceInfo::DefaultArgument:
// No original location as it's not actually in any source file
case GeneratedSourceInfo::ReplacedFunctionBody:
// There's not really any "original" location for locations within
// replaced function bodies. The body is actually different code to the
// original file.
case GeneratedSourceInfo::PrettyPrinted:
case GeneratedSourceInfo::AttributeFromClang:
// No original location, return the original buffer/location
return {startBufferID, startRange};
}
}

return {bufferID, range};
}

ArrayRef<SourceFile *>
PrimarySourceFilesRequest::evaluate(Evaluator &evaluator,
ModuleDecl *mod) const {
Expand Down Expand Up @@ -1431,11 +1389,11 @@ SourceFile::getExternalRawLocsForDecl(const Decl *D) const {
bool InGeneratedBuffer =
!SM.rangeContainsTokenLoc(SM.getRangeForBuffer(BufferID), MainLoc);
if (InGeneratedBuffer) {
unsigned UnderlyingBufferID;
std::tie(UnderlyingBufferID, MainLoc) =
D->getModuleContext()->getOriginalLocation(MainLoc);
if (BufferID != UnderlyingBufferID)
return std::nullopt;
if (auto R = getUnexpandedMacroRange(SM, MainLoc)) {
if (BufferID != SM.findBufferContainingLoc(R.Start))
return std::nullopt;
MainLoc = R.Start;
}
}

auto setLoc = [&](ExternalSourceLocs::RawLoc &RawLoc, SourceLoc Loc) {
Expand Down
35 changes: 16 additions & 19 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "swift/Basic/Defer.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/StringExtras.h"
#include "swift/Basic/Version.h"
Expand Down Expand Up @@ -8647,17 +8648,16 @@ bool importer::hasSameUnderlyingType(const clang::Type *a,
}

SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
ModuleDecl &module,
StringRef attributeText,
bool cached
) {
Decl *MappedDecl, StringRef attributeText, bool cached) {
auto *module = MappedDecl->getDeclContext()->getParentModule();

::TinyPtrVector<SourceFile *> *sourceFiles = nullptr;
if (cached) {
sourceFiles = &ClangSwiftAttrSourceFiles[attributeText];

// Check whether we've already created a source file.
for (auto sourceFile : *sourceFiles) {
if (sourceFile->getParentModule() == &module)
if (sourceFile->getParentModule() == module)
return *sourceFile;
}
}
Expand All @@ -8667,20 +8667,17 @@ SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
auto &sourceMgr = SwiftContext.SourceMgr;
auto bufferID = sourceMgr.addMemBufferCopy(attributeText);

// Note that this is for an attribute.
sourceMgr.setGeneratedSourceInfo(
bufferID,
{
GeneratedSourceInfo::AttributeFromClang,
CharSourceRange(),
sourceMgr.getRangeForBuffer(bufferID),
&module
}
);
auto info = GeneratedSourceInfo{GeneratedSourceInfo::AttributeFromClang,
CharSourceRange(),
sourceMgr.getRangeForBuffer(bufferID)};
info.astNode = static_cast<void *>(module);
info.clangNode = MappedDecl->getClangNode();

sourceMgr.setGeneratedSourceInfo(bufferID, info);

// Create the source file.
auto sourceFile = new (SwiftContext)
SourceFile(module, SourceFileKind::Library, bufferID);
auto sourceFile =
new (SwiftContext) SourceFile(*module, SourceFileKind::Library, bufferID);

if (cached)
sourceFiles->push_back(sourceFile);
Expand All @@ -8703,8 +8700,8 @@ void ClangImporter::Implementation::importNontrivialAttribute(
bool cached = true;
while (true) {
// Dig out a source file we can use for parsing.
auto &sourceFile = getClangSwiftAttrSourceFile(
*MappedDecl->getDeclContext()->getParentModule(), AttrString, cached);
auto &sourceFile =
getClangSwiftAttrSourceFile(MappedDecl, AttrString, cached);

auto topLevelDecls = sourceFile.getTopLevelDecls();

Expand Down
6 changes: 3 additions & 3 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1057,9 +1057,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
StringRef getSwiftNameFromClangName(StringRef name);

/// Retrieve the placeholder source file for use in parsing Swift attributes
/// in the given module.
SourceFile &getClangSwiftAttrSourceFile(
ModuleDecl &module, StringRef attributeText, bool cached);
/// of the given Decl.
SourceFile &getClangSwiftAttrSourceFile(Decl *MappedDecl,
StringRef attributeText, bool cached);

/// Create attribute with given text and attach it to decl, creating or
/// retrieving a chached source file as needed.
Expand Down
11 changes: 7 additions & 4 deletions lib/Index/Index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include <optional>
#include <tuple>

using namespace swift;
Expand Down Expand Up @@ -1093,10 +1094,12 @@ class IndexSwiftASTWalker : public SourceEntityWalker {
!SrcMgr.rangeContainsTokenLoc(SrcMgr.getRangeForBuffer(bufferID), loc);

if (inGeneratedBuffer) {
std::tie(bufferID, loc) = CurrentModule->getOriginalLocation(loc);
if (BufferID.value() != bufferID) {
assert(false && "Location is not within file being indexed");
return std::nullopt;
if (auto unexpandedRange = getUnexpandedMacroRange(SrcMgr, loc)) {
loc = unexpandedRange.Start;
if (bufferID != SrcMgr.findBufferContainingLoc(loc)) {
assert(false && "Location should be within file being indexed");
return std::nullopt;
}
}
}

Expand Down
61 changes: 61 additions & 0 deletions test/SourceKit/Macros/clang-overload-cursor-info.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
//--- Main.swift
import FromClang // NOTE: line offset = -4

// REQUIRES: swift_feature_SafeInteropWrappers
// REQUIRES: swift_feature_LifetimeDependence

// The macro-generated interface we're looking up source info for
// (this is more so for documentation than checking correctness)
//
// INTERFACE: @_alwaysEmitIntoClient @_disfavoredOverload public func hasBufferOverload(_ p: UnsafeMutableBufferPointer<Int32>)
// INTERFACE: @{{_?}}lifetime(p: copy p)
// INTERFACE-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func hasSpanOverload(_ p: inout MutableSpan<Int32>)
// RUN: %target-swift-ide-test \
// RUN: -print-module -module-to-print=FromClang -source-filename=x \
// RUN: -plugin-path %swift-plugin-dir -I %t/Inputs \
// RUN: -enable-experimental-feature SafeInteropWrappers \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: | %FileCheck %t/Main.swift --check-prefix INTERFACE

@inlinable
public func callWithBufferPtr(_ p: UnsafeMutableBufferPointer<CInt>) {
hasBufferOverload(p)
// RUN: %sourcekitd-test -req=cursor -pos=%(line-4):3 %t/Main.swift -- -I %t/Inputs %t/Main.swift \
// RUN: -enable-experimental-feature SafeInteropWrappers \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: | %FileCheck %t/Inputs/from-clang.h --check-prefix BUFFER-OVERLOAD
}

@available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
@lifetime(p: copy p)
@inlinable
public func callReturnLifetimeBound(_ p: inout MutableSpan<CInt>) {
hasSpanOverload(p)
// RUN: %sourcekitd-test -req=cursor -pos=%(line-4):3 %t/Main.swift -- -I %t/Inputs %t/Main.swift \
// RUN: -enable-experimental-feature SafeInteropWrappers \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: | %FileCheck %t/Inputs/from-clang.h --check-prefix SPAN-OVERLOAD
}

//--- Inputs/module.modulemap
module FromClang {
header "from-clang.h"
export *
}

//--- Inputs/from-clang.h
#pragma once

#define __counted_by(x) __attribute__((__counted_by__(x)))
#define __noescape __attribute__((noescape))
#define __lifetimebound __attribute__((lifetimebound))

void hasBufferOverload(int len, int * __counted_by(len) p);
// BUFFER-OVERLOAD: source.lang.swift.ref.function.free
// BUFFER-OVERLOAD-SAME: from-clang.h:[[@LINE-2]]

void hasSpanOverload(int len, int * __counted_by(len) __noescape p);
// SPAN-OVERLOAD: source.lang.swift.ref.function.free
// SPAN-OVERLOAD-SAME: from-clang.h:[[@LINE-2]]
Loading