92 changes: 8 additions & 84 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,82 +643,6 @@ Error RewriteInstance::discoverStorage() {
return Error::success();
}

void RewriteInstance::parseBuildID() {
if (!BuildIDSection)
return;

StringRef Buf = BuildIDSection->getContents();

// Reading notes section (see Portable Formats Specification, Version 1.1,
// pg 2-5, section "Note Section").
DataExtractor DE =
DataExtractor(Buf,
/*IsLittleEndian=*/true, InputFile->getBytesInAddress());
uint64_t Offset = 0;
if (!DE.isValidOffset(Offset))
return;
uint32_t NameSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t DescSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t Type = DE.getU32(&Offset);

LLVM_DEBUG(dbgs() << "NameSz = " << NameSz << "; DescSz = " << DescSz
<< "; Type = " << Type << "\n");

// Type 3 is a GNU build-id note section
if (Type != 3)
return;

StringRef Name = Buf.slice(Offset, Offset + NameSz);
Offset = alignTo(Offset + NameSz, 4);
if (Name.substr(0, 3) != "GNU")
return;

BuildID = Buf.slice(Offset, Offset + DescSz);
}

std::optional<std::string> RewriteInstance::getPrintableBuildID() const {
if (BuildID.empty())
return std::nullopt;

std::string Str;
raw_string_ostream OS(Str);
const unsigned char *CharIter = BuildID.bytes_begin();
while (CharIter != BuildID.bytes_end()) {
if (*CharIter < 0x10)
OS << "0";
OS << Twine::utohexstr(*CharIter);
++CharIter;
}
return OS.str();
}

void RewriteInstance::patchBuildID() {
raw_fd_ostream &OS = Out->os();

if (BuildID.empty())
return;

size_t IDOffset = BuildIDSection->getContents().rfind(BuildID);
assert(IDOffset != StringRef::npos && "failed to patch build-id");

uint64_t FileOffset = getFileOffsetForAddress(BuildIDSection->getAddress());
if (!FileOffset) {
BC->errs()
<< "BOLT-WARNING: Non-allocatable build-id will not be updated.\n";
return;
}

char LastIDByte = BuildID[BuildID.size() - 1];
LastIDByte ^= 1;
OS.pwrite(&LastIDByte, 1, FileOffset + IDOffset + BuildID.size() - 1);

BC->outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";
}

Error RewriteInstance::run() {
assert(BC && "failed to create a binary context");

Expand Down Expand Up @@ -1977,7 +1901,6 @@ Error RewriteInstance::readSpecialSections() {
".rela" + std::string(BC->getMainCodeSectionName()));
HasSymbolTable = (bool)BC->getUniqueSectionByName(".symtab");
EHFrameSection = BC->getUniqueSectionByName(".eh_frame");
BuildIDSection = BC->getUniqueSectionByName(".note.gnu.build-id");

if (ErrorOr<BinarySection &> BATSec =
BC->getUniqueSectionByName(BoltAddressTranslation::SECTION_NAME)) {
Expand Down Expand Up @@ -2035,10 +1958,7 @@ Error RewriteInstance::readSpecialSections() {
report_error("expected valid eh_frame section", EHFrameOrError.takeError());
CFIRdWrt.reset(new CFIReaderWriter(*BC, *EHFrameOrError.get()));

// Parse build-id
parseBuildID();
if (std::optional<std::string> FileBuildID = getPrintableBuildID())
BC->setFileBuildID(*FileBuildID);
processSectionMetadata();

// Read .dynamic/PT_DYNAMIC.
return readELFDynamic();
Expand Down Expand Up @@ -3218,14 +3138,20 @@ void RewriteInstance::initializeMetadataManager() {
if (BC->IsLinuxKernel)
MetadataManager.registerRewriter(createLinuxKernelRewriter(*BC));

MetadataManager.registerRewriter(createBuildIDRewriter(*BC));

MetadataManager.registerRewriter(createPseudoProbeRewriter(*BC));

MetadataManager.registerRewriter(createSDTRewriter(*BC));
}

void RewriteInstance::processMetadataPreCFG() {
void RewriteInstance::processSectionMetadata() {
initializeMetadataManager();

MetadataManager.runSectionInitializers();
}

void RewriteInstance::processMetadataPreCFG() {
MetadataManager.runInitializersPreCFG();

processProfileDataPreCFG();
Expand Down Expand Up @@ -5772,8 +5698,6 @@ void RewriteInstance::rewriteFile() {
// Update symbol tables.
patchELFSymTabs();

patchBuildID();

if (opts::EnableBAT)
encodeBATSection();

Expand Down
10 changes: 0 additions & 10 deletions bolt/test/Inputs/lsda.ldscript

This file was deleted.

710 changes: 710 additions & 0 deletions bolt/test/X86/Inputs/dwarf4-df-input-lowpc-ranges-other.s

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
; RUN: rm -rf %t
; RUN: mkdir %t
; RUN: cd %t
; RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf4-df-input-lowpc-ranges-main.s \
; RUN: -split-dwarf-file=main.dwo -o main.o
; RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf4-df-input-lowpc-ranges-other.s \
; RUN: -split-dwarf-file=mainOther.dwo -o other.o
; RUN: %clang %cflags -gdwarf-4 -gsplit-dwarf=split main.o other.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --show-form --verbose --debug-ranges main.exe.bolt &> %t/foo.txt
; RUN: llvm-dwarfdump --show-form --verbose --debug-info main.exe.bolt >> %t/foo.txt
; RUN: cat %t/foo.txt | FileCheck -check-prefix=BOLT %s
; RUN: not llvm-dwarfdump --show-form --verbose --debug-info main.dwo.dwo mainOther.dwo.dwo &> %t/mainddwodwo.txt
; RUN: cat %t/mainddwodwo.txt | FileCheck -check-prefix=BOLT-DWO-MAIN %s

;; Tests that BOLT correctly handles Skeleton CU which has DW_AT_low_pc/DW_AT_ranges as input and handles multiple CUs with ranges.

; BOLT: .debug_ranges
; BOLT-NEXT: 00000000 <End of list>
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010 <End of list>
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050 <End of list>
; BOLT-NEXT: 00000090 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]]
; BOLT-NEXT: 00000090 <End of list>
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110 <End of list>
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150 <End of list>
; BOLT-NEXT: 00000190 [[#%.16x,ADDR8:]] [[#%.16x,ADDRB8:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR9:]] [[#%.16x,ADDRB9:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR10:]] [[#%.16x,ADDRB10:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR11:]] [[#%.16x,ADDRB11:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR12:]] [[#%.16x,ADDRB12:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR13:]] [[#%.16x,ADDRB13:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR14:]] [[#%.16x,ADDRB14:]]
; BOLT-NEXT: 00000190 <End of list>

; BOLT: DW_TAG_compile_unit
; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "main.dwo.dwo")
; BOLT-NEXT: DW_AT_GNU_dwo_id
; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000010)
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000090
; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDRB1]])
; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDRB2]])
; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDRB3]])
; BOLT-NEXT: [0x[[#ADDR4]], 0x[[#ADDRB4]])
; BOLT-NEXT: [0x[[#ADDR5]], 0x[[#ADDRB5]])
; BOLT-NEXT: [0x[[#ADDR6]], 0x[[#ADDRB6]])
; BOLT-NEXT: [0x[[#ADDR7]], 0x[[#ADDRB7]])
; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000000)

; BOLT: DW_TAG_compile_unit
; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "mainOther.dwo.dwo")
; BOLT-NEXT: DW_AT_GNU_dwo_id
; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000110)
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000190
; BOLT-NEXT: [0x[[#ADDR8]], 0x[[#ADDRB8]])
; BOLT-NEXT: [0x[[#ADDR9]], 0x[[#ADDRB9]])
; BOLT-NEXT: [0x[[#ADDR10]], 0x[[#ADDRB10]])
; BOLT-NEXT: [0x[[#ADDR11]], 0x[[#ADDRB11]])
; BOLT-NEXT: [0x[[#ADDR12]], 0x[[#ADDRB12]])
; BOLT-NEXT: [0x[[#ADDR13]], 0x[[#ADDRB13]])
; BOLT-NEXT: [0x[[#ADDR14]], 0x[[#ADDRB14]])
; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000018)

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040
8 changes: 4 additions & 4 deletions bolt/test/lsda-section-name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// disassembled by BOLT.

// RUN: %clang++ %cxxflags -O3 -no-pie -c %s -o %t.o
// RUN: %clang++ %cxxflags -no-pie -fuse-ld=lld %t.o -o %t.exe \
// RUN: -Wl,-q -Wl,--script=%S/Inputs/lsda.ldscript
// RUN: llvm-readelf -SW %t.exe | FileCheck %s
// RUN: llvm-bolt %t.exe -o %t.bolt
// RUN: %clang++ %cxxflags -O3 -no-pie -fuse-ld=lld %t.o -o %t
// RUN: llvm-objcopy --rename-section .gcc_except_table=.gcc_except_table.main %t
// RUN: llvm-readelf -SW %t | FileCheck %s
// RUN: llvm-bolt %t -o %t.bolt

// CHECK: .gcc_except_table.main

Expand Down
14 changes: 8 additions & 6 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
}

void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType =
hasType(pointerType(pointee(matchers::isSimpleChar())));
Finder->addMatcher(
callExpr(argumentCountAtLeast(1),
hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
matchers::matchesAnyListedName(
StrFormatLikeFunctions))
.bind("func_decl")))
callExpr(
argumentCountAtLeast(1), hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(
unless(cxxMethodDecl()), hasParameter(0, CharPointerType),
matchers::matchesAnyListedName(StrFormatLikeFunctions))
.bind("func_decl")))
.bind("strformat"),
this);
}
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,15 @@ unusedReturnValue(clang::ast_matchers::StatementMatcher MatchedCallExpr) {
}

void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType =
hasType(pointerType(pointee(matchers::isSimpleChar())));
if (!PrintfLikeFunctions.empty())
Finder->addMatcher(
unusedReturnValue(
callExpr(argumentCountAtLeast(1),
hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
hasParameter(0, CharPointerType),
matchers::matchesAnyListedName(
PrintfLikeFunctions))
.bind("func_decl")))
Expand All @@ -113,6 +116,7 @@ void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
callExpr(argumentCountAtLeast(2),
hasArgument(1, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
hasParameter(1, CharPointerType),
matchers::matchesAnyListedName(
FprintfLikeFunctions))
.bind("func_decl")))
Expand Down
8 changes: 5 additions & 3 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,11 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
assert(ArgsOffset <= NumArgs);
FormatExpr = llvm::dyn_cast<StringLiteral>(
Args[FormatArgOffset]->IgnoreImplicitAsWritten());
assert(FormatExpr);
if (!FormatExpr->isOrdinary())
return; // No wide string support yet
if (!FormatExpr || !FormatExpr->isOrdinary()) {
// Function must have a narrow string literal as its first argument.
conversionNotPossible("first argument is not a narrow string literal");
return;
}
PrintfFormatString = FormatExpr->getString();

// Assume that the output will be approximately the same size as the input,
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/clang-tidy/utils/Matchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isPointerToConst) {
return pointerType(pointee(qualType(isConstQualified())));
}

// Returns QualType matcher for target char type only.
AST_MATCHER(QualType, isSimpleChar) {
const auto ActualType = Node.getTypePtr();
return ActualType &&
(ActualType->isSpecificBuiltinType(BuiltinType::Char_S) ||
ActualType->isSpecificBuiltinType(BuiltinType::Char_U));
}

AST_MATCHER(Expr, hasUnevaluatedContext) {
if (isa<CXXNoexceptExpr>(Node) || isa<RequiresExpr>(Node))
return true;
Expand Down
150 changes: 93 additions & 57 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,11 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind,
// looked up in the context containing the function/method.
// FIXME: Drop attributes in function signature.
llvm::Expected<std::string>
getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
const syntax::TokenBuffer &TokBuf,
const HeuristicResolver *Resolver) {
auto &AST = FD->getASTContext();
auto &SM = AST.getSourceManager();
auto TargetContext = findContextForNS(TargetNamespace, FD->getDeclContext());
if (!TargetContext)
return error("define outline: couldn't find a context for target");

llvm::Error Errors = llvm::Error::success();
tooling::Replacements DeclarationCleanups;
Expand Down Expand Up @@ -216,7 +213,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
}
const NamedDecl *ND = Ref.Targets.front();
const std::string Qualifier =
getQualification(AST, *TargetContext,
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()), ND);
if (auto Err = DeclarationCleanups.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
Expand All @@ -232,7 +229,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
if (const auto *Destructor = llvm::dyn_cast<CXXDestructorDecl>(FD)) {
if (auto Err = DeclarationCleanups.add(tooling::Replacement(
SM, Destructor->getLocation(), 0,
getQualification(AST, *TargetContext,
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()),
Destructor))))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
Expand Down Expand Up @@ -319,29 +316,9 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
}

struct InsertionPoint {
std::string EnclosingNamespace;
const DeclContext *EnclosingNamespace = nullptr;
size_t Offset;
};
// Returns the most natural insertion point for \p QualifiedName in \p Contents.
// This currently cares about only the namespace proximity, but in feature it
// should also try to follow ordering of declarations. For example, if decls
// come in order `foo, bar, baz` then this function should return some point
// between foo and baz for inserting bar.
llvm::Expected<InsertionPoint> getInsertionPoint(llvm::StringRef Contents,
llvm::StringRef QualifiedName,
const LangOptions &LangOpts) {
auto Region = getEligiblePoints(Contents, QualifiedName, LangOpts);

assert(!Region.EligiblePoints.empty());
// FIXME: This selection can be made smarter by looking at the definition
// locations for adjacent decls to Source. Unfortunately pseudo parsing in
// getEligibleRegions only knows about namespace begin/end events so we
// can't match function start/end positions yet.
auto Offset = positionToOffset(Contents, Region.EligiblePoints.back());
if (!Offset)
return Offset.takeError();
return InsertionPoint{Region.EnclosingNamespace, *Offset};
}

// Returns the range that should be deleted from declaration, which always
// contains function body. In addition to that it might contain constructor
Expand Down Expand Up @@ -409,14 +386,9 @@ class DefineOutline : public Tweak {
}

bool prepare(const Selection &Sel) override {
// Bail out if we are not in a header file.
// FIXME: We might want to consider moving method definitions below class
// definition even if we are inside a source file.
if (!isHeaderFile(Sel.AST->getSourceManager().getFilename(Sel.Cursor),
Sel.AST->getLangOpts()))
return false;

SameFile = !isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts());
Source = getSelectedFunction(Sel.ASTSelection.commonAncestor());

// Bail out if the selection is not a in-line function definition.
if (!Source || !Source->doesThisDeclarationHaveABody() ||
Source->isOutOfLine())
Expand All @@ -429,19 +401,24 @@ class DefineOutline : public Tweak {
if (Source->getTemplateSpecializationInfo())
return false;

if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;

// The refactoring is meaningless for unnamed classes and definitions
// within unnamed namespaces.
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
if (auto *ND = llvm::dyn_cast<NamedDecl>(DC)) {
if (ND->getDeclName().isEmpty())
return false;
}
auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source);
if (!MD) {
// Can't outline free-standing functions in the same file.
return !SameFile;
}

// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
if (auto *ND = llvm::dyn_cast<NamedDecl>(DC)) {
if (ND->getDeclName().isEmpty() &&
(!SameFile || !llvm::dyn_cast<NamespaceDecl>(ND)))
return false;
}
}

Expand All @@ -453,8 +430,8 @@ class DefineOutline : public Tweak {

Expected<Effect> apply(const Selection &Sel) override {
const SourceManager &SM = Sel.AST->getSourceManager();
auto CCFile = getSourceFile(Sel.AST->tuPath(), Sel);

auto CCFile = SameFile ? Sel.AST->tuPath().str()
: getSourceFile(Sel.AST->tuPath(), Sel);
if (!CCFile)
return error("Couldn't find a suitable implementation file.");
assert(Sel.FS && "FS Must be set in apply");
Expand All @@ -464,8 +441,7 @@ class DefineOutline : public Tweak {
if (!Buffer)
return llvm::errorCodeToError(Buffer.getError());
auto Contents = Buffer->get()->getBuffer();
auto InsertionPoint = getInsertionPoint(
Contents, Source->getQualifiedNameAsString(), Sel.AST->getLangOpts());
auto InsertionPoint = getInsertionPoint(Contents, Sel);
if (!InsertionPoint)
return InsertionPoint.takeError();

Expand Down Expand Up @@ -499,17 +475,77 @@ class DefineOutline : public Tweak {
HeaderUpdates = HeaderUpdates.merge(*DelInline);
}

auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(), HeaderUpdates);
if (!HeaderFE)
return HeaderFE.takeError();

Effect->ApplyEdits.try_emplace(HeaderFE->first,
std::move(HeaderFE->second));
if (SameFile) {
tooling::Replacements &R = Effect->ApplyEdits[*CCFile].Replacements;
R = R.merge(HeaderUpdates);
} else {
auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(), HeaderUpdates);
if (!HeaderFE)
return HeaderFE.takeError();
Effect->ApplyEdits.try_emplace(HeaderFE->first,
std::move(HeaderFE->second));
}
return std::move(*Effect);
}

// Returns the most natural insertion point for \p QualifiedName in \p
// Contents. This currently cares about only the namespace proximity, but in
// feature it should also try to follow ordering of declarations. For example,
// if decls come in order `foo, bar, baz` then this function should return
// some point between foo and baz for inserting bar.
// FIXME: The selection can be made smarter by looking at the definition
// locations for adjacent decls to Source. Unfortunately pseudo parsing in
// getEligibleRegions only knows about namespace begin/end events so we
// can't match function start/end positions yet.
llvm::Expected<InsertionPoint> getInsertionPoint(llvm::StringRef Contents,
const Selection &Sel) {
// If the definition goes to the same file and there is a namespace,
// we should (and, in the case of anonymous namespaces, need to)
// put the definition into the original namespace block.
if (SameFile) {
auto *Klass = Source->getDeclContext()->getOuterLexicalRecordContext();
if (!Klass)
return error("moving to same file not supported for free functions");
const SourceLocation EndLoc = Klass->getBraceRange().getEnd();
const auto &TokBuf = Sel.AST->getTokens();
auto Tokens = TokBuf.expandedTokens();
auto It = llvm::lower_bound(
Tokens, EndLoc, [](const syntax::Token &Tok, SourceLocation EndLoc) {
return Tok.location() < EndLoc;
});
while (It != Tokens.end()) {
if (It->kind() != tok::semi) {
++It;
continue;
}
unsigned Offset = Sel.AST->getSourceManager()
.getDecomposedLoc(It->endLocation())
.second;
return InsertionPoint{Klass->getEnclosingNamespaceContext(), Offset};
}
return error(
"failed to determine insertion location: no end of class found");
}

auto Region = getEligiblePoints(
Contents, Source->getQualifiedNameAsString(), Sel.AST->getLangOpts());

assert(!Region.EligiblePoints.empty());
auto Offset = positionToOffset(Contents, Region.EligiblePoints.back());
if (!Offset)
return Offset.takeError();

auto TargetContext =
findContextForNS(Region.EnclosingNamespace, Source->getDeclContext());
if (!TargetContext)
return error("define outline: couldn't find a context for target");

return InsertionPoint{*TargetContext, *Offset};
}

private:
const FunctionDecl *Source = nullptr;
bool SameFile = false;
};

REGISTER_TWEAK(DefineOutline)
Expand Down
10 changes: 7 additions & 3 deletions clang-tools-extra/clangd/unittests/ReplayPeambleTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
Expand All @@ -42,7 +41,11 @@
#include <memory>
#include <vector>

namespace clang::clangd {
namespace clang {

class Module;

namespace clangd {
namespace {
struct Inclusion {
Inclusion(const SourceManager &SM, SourceLocation HashLoc,
Expand Down Expand Up @@ -170,4 +173,5 @@ TEST(ReplayPreambleTest, IncludesAndSkippedFiles) {
}
}
} // namespace
} // namespace clang::clangd
} // namespace clangd
} // namespace clang
73 changes: 71 additions & 2 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,47 @@ TWEAK_TEST(DefineOutline);

TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
FileName = "Test.cpp";
// Not available unless in a header file.
// Not available for free function unless in a header file.
EXPECT_UNAVAILABLE(R"cpp(
[[void [[f^o^o]]() [[{
return;
}]]]])cpp");

// Available in soure file.
EXPECT_AVAILABLE(R"cpp(
struct Foo {
void f^oo() {}
};
)cpp");

// Available within named namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace N {
struct Foo {
void f^oo() {}
};
} // namespace N
)cpp");

// Available within anonymous namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace {
struct Foo {
void f^oo() {}
};
} // namespace
)cpp");

// Not available for out-of-line method.
EXPECT_UNAVAILABLE(R"cpp(
class Bar {
void baz();
};
[[void [[Bar::[[b^a^z]]]]() [[{
return;
}]]]])cpp");

FileName = "Test.hpp";
// Not available unless function name or fully body is selected.
EXPECT_UNAVAILABLE(R"cpp(
Expand Down Expand Up @@ -100,7 +135,7 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
};
)cpp");

// Not available on definitions within unnamed namespaces
// Not available on definitions in header file within unnamed namespaces
EXPECT_UNAVAILABLE(R"cpp(
namespace {
struct Foo {
Expand Down Expand Up @@ -349,6 +384,40 @@ TEST_F(DefineOutlineTest, ApplyTest) {
}
}

TEST_F(DefineOutlineTest, InCppFile) {
FileName = "Test.cpp";

struct {
llvm::StringRef Test;
llvm::StringRef ExpectedSource;
} Cases[] = {
{
R"cpp(
namespace foo {
namespace {
struct Foo { void ba^r() {} };
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp",
R"cpp(
namespace foo {
namespace {
struct Foo { void bar() ; };void Foo::bar() {}
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp"},
};

for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);
}
}

TEST_F(DefineOutlineTest, HandleMacros) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-starts-ends-with>` check to also handle
calls to ``compare`` method.

- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to not crash if the
format string parameter of the function to be replaced is not of the
expected type.

- Improved :doc:`modernize-use-using <clang-tidy/checks/modernize/use-using>`
check by adding support for detection of typedefs declared on function level.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
Expand Down Expand Up @@ -50,3 +50,17 @@ std::string A(const std::string &in)
{
return "_" + in;
}

// Issue #92896: Ensure that the check doesn't assert if the argument is
// promoted to something that isn't a string.
struct S {
S(...);
};
std::string bad_format_type_strprintf(const S &, ...);

std::string unsupported_format_parameter_type()
{
// No fixes here because the format parameter of the function called is not a
// string.
return bad_format_type_strprintf("");
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-std-print %t -- \
// RUN: -config="{CheckOptions: \
// RUN: { \
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2', \
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2' \
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2; bad_format_type_printf', \
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2; bad_format_type_fprintf' \
// RUN: } \
// RUN: }" \
// RUN: -- -isystem %clang_tidy_headers
Expand Down Expand Up @@ -86,3 +86,25 @@ void no_name(const std::string &in)
{
"A" + in;
}

int myprintf(const wchar_t *, ...);

void wide_string_not_supported() {
myprintf(L"wide string %s", L"string");
}

// Issue #92896: Ensure that the check doesn't assert if the argument is
// promoted to something that isn't a string.
struct S {
S(...) {}
};
int bad_format_type_printf(const S &, ...);
int bad_format_type_fprintf(FILE *, const S &, ...);

void unsupported_format_parameter_type()
{
// No fixes here because the format parameter of the function called is not a
// string.
bad_format_type_printf("Hello %s", "world");
bad_format_type_fprintf(stderr, "Hello %s", "world");
}
21 changes: 11 additions & 10 deletions clang/cmake/caches/CrossWinToARMLinux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@
# on Windows platform.
#
# NOTE: the build requires a development ARM Linux root filesystem to use
# proper target platform depended library and header files.
# proper target platform depended library and header files:
# - create <full-path-to-clang-configs> directory and put the clang configuration
# file named <TOOLCHAIN_TARGET_TRIPLE>.cfg into it.
# - add the `--sysroot=<path-to-develop-arm-linux-root-fs>` argument into
# this configuration file.
# - add other necessary target depended clang arguments there,
# such as '-mcpu=cortex-a78' & etc.
#
# See more details here: https://clang.llvm.org/docs/UsersManual.html#configuration-files
#
# Configure:
# cmake -G Ninja ^
# -DTOOLCHAIN_TARGET_TRIPLE=armv7-unknown-linux-gnueabihf ^
# -DTOOLCHAIN_TARGET_TRIPLE=aarch64-unknown-linux-gnu ^
# -DCMAKE_INSTALL_PREFIX=../install ^
# -DDEFAULT_SYSROOT=<path-to-develop-arm-linux-root-fs> ^
# -DLLVM_AR=<llvm_obj_root>/bin/llvm-ar[.exe] ^
# -DCLANG_CONFIG_FILE_USER_DIR=<full-path-to-clang-configs> ^
# -DCMAKE_CXX_FLAGS="-D__OPTIMIZE__" ^
# -DREMOTE_TEST_HOST="<hostname>" ^
# -DREMOTE_TEST_USER="<ssh_user_name>" ^
Expand Down Expand Up @@ -43,10 +50,6 @@ get_filename_component(LLVM_PROJECT_DIR
"${CMAKE_CURRENT_LIST_DIR}/../../../"
ABSOLUTE)

if (NOT DEFINED DEFAULT_SYSROOT)
message(WARNING "DEFAULT_SYSROOT must be specified for the cross toolchain build.")
endif()

if (NOT DEFINED LLVM_ENABLE_ASSERTIONS)
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
endif()
Expand Down Expand Up @@ -136,7 +139,6 @@ endif()
set(LLVM_BUILTIN_TARGETS "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")

set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSTEM_NAME "Linux" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_INSTALL_RPATH "${RUNTIMES_INSTALL_RPATH}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_CMAKE_DIR "${LLVM_PROJECT_DIR}/llvm/cmake/modules" CACHE PATH "")
Expand All @@ -156,7 +158,6 @@ set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_ENABLE_RUNTIMES "${LLVM_ENABLE_RUNTIMES}" CACHE STRING "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSTEM_NAME "Linux" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_INSTALL_RPATH "${RUNTIMES_INSTALL_RPATH}" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")

Expand Down
70 changes: 35 additions & 35 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1799,8 +1799,8 @@ the configuration (without a prefix: ``Auto``).
Never merge functions into a single line.

* ``SFS_InlineOnly`` (in configuration: ``InlineOnly``)
Only merge functions defined inside a class. Same as "inline",
except it does not implies "empty": i.e. top level empty functions
Only merge functions defined inside a class. Same as ``inline``,
except it does not implies ``empty``: i.e. top level empty functions
are not merged either.

.. code-block:: c++
Expand All @@ -1825,7 +1825,7 @@ the configuration (without a prefix: ``Auto``).
}

* ``SFS_Inline`` (in configuration: ``Inline``)
Only merge functions defined inside a class. Implies "empty".
Only merge functions defined inside a class. Implies ``empty``.

.. code-block:: c++

Expand Down Expand Up @@ -2042,7 +2042,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
AttributeMacros: ['__capability', '__output', '__unused']
AttributeMacros: [__capability, __output, __unused]
.. _BinPackArguments:

Expand Down Expand Up @@ -3802,7 +3802,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
ForEachMacros: ['RANGES_FOR', 'FOREACH']
ForEachMacros: [RANGES_FOR, FOREACH]
For example: BOOST_FOREACH.

Expand All @@ -3825,7 +3825,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
IfMacros: ['IF']
IfMacros: [IF]
For example: `KJ_IF_MAYBE
<https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#maybes>`_
Expand Down Expand Up @@ -4374,7 +4374,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
JavaImportGroups: ['com.example', 'com', 'org']
JavaImportGroups: [com.example, com, org]
.. code-block:: java
Expand Down Expand Up @@ -4438,7 +4438,7 @@ the configuration (without a prefix: ``Auto``).
VeryLongImportsAreAnnoying,
VeryLongImportsAreAnnoying,
VeryLongImportsAreAnnoying,
} from 'some/module.js'
} from "some/module.js"
false:
import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js"
Expand Down Expand Up @@ -5088,7 +5088,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
QualifierOrder: ['inline', 'static', 'type', 'const']
QualifierOrder: [inline, static, type, const]
.. code-block:: c++
Expand Down Expand Up @@ -5117,16 +5117,16 @@ the configuration (without a prefix: ``Auto``).

.. note::

it MUST contain 'type'.
It **must** contain ``type``.

Items to the left of 'type' will be placed to the left of the type and
aligned in the order supplied. Items to the right of 'type' will be
Items to the left of ``type`` will be placed to the left of the type and
aligned in the order supplied. Items to the right of ``type`` will be
placed to the right of the type and aligned in the order supplied.


.. code-block:: yaml
QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ]
QualifierOrder: [inline, static, type, const, volatile]
.. _RawStringFormats:

Expand All @@ -5138,10 +5138,10 @@ the configuration (without a prefix: ``Auto``).
name will be reformatted assuming the specified language based on the
style for that language defined in the .clang-format file. If no style has
been defined in the .clang-format file for the specific language, a
predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not
found, the formatting is based on llvm style. A matching delimiter takes
precedence over a matching enclosing function name for determining the
language of the raw string contents.
predefined style given by ``BasedOnStyle`` is used. If ``BasedOnStyle`` is
not found, the formatting is based on ``LLVM`` style. A matching delimiter
takes precedence over a matching enclosing function name for determining
the language of the raw string contents.

If a canonical delimiter is specified, occurrences of other delimiters for
the same language will be updated to the canonical if possible.
Expand All @@ -5156,17 +5156,17 @@ the configuration (without a prefix: ``Auto``).
RawStringFormats:
- Language: TextProto
Delimiters:
- 'pb'
- 'proto'
- pb
- proto
EnclosingFunctions:
- 'PARSE_TEXT_PROTO'
- PARSE_TEXT_PROTO
BasedOnStyle: google
- Language: Cpp
Delimiters:
- 'cc'
- 'cpp'
BasedOnStyle: llvm
CanonicalDelimiter: 'cc'
- cc
- cpp
BasedOnStyle: LLVM
CanonicalDelimiter: cc
.. _ReferenceAlignment:

Expand Down Expand Up @@ -5533,7 +5533,7 @@ the configuration (without a prefix: ``Auto``).

This determines the maximum length of short namespaces by counting
unwrapped lines (i.e. containing neither opening nor closing
namespace brace) and makes "FixNamespaceComments" omit adding
namespace brace) and makes ``FixNamespaceComments`` omit adding
end comments for those.

.. code-block:: c++
Expand Down Expand Up @@ -5645,7 +5645,7 @@ the configuration (without a prefix: ``Auto``).

* ``SUD_Lexicographic`` (in configuration: ``Lexicographic``)
Using declarations are sorted in the order defined as follows:
Split the strings by "::" and discard any initial empty strings. Sort
Split the strings by ``::`` and discard any initial empty strings. Sort
the lists of names lexicographically, and within those groups, names are
in case-insensitive lexicographic order.

Expand All @@ -5659,7 +5659,7 @@ the configuration (without a prefix: ``Auto``).

* ``SUD_LexicographicNumeric`` (in configuration: ``LexicographicNumeric``)
Using declarations are sorted in the order defined as follows:
Split the strings by "::" and discard any initial empty strings. The
Split the strings by ``::`` and discard any initial empty strings. The
last element of each list is a non-namespace name; all others are
namespace names. Sort the lists of names lexicographically, where the
sort order of individual names is that all non-namespace names come
Expand Down Expand Up @@ -5699,7 +5699,7 @@ the configuration (without a prefix: ``Auto``).
.. _SpaceAfterTemplateKeyword:

**SpaceAfterTemplateKeyword** (``Boolean``) :versionbadge:`clang-format 4` :ref:`¶ <SpaceAfterTemplateKeyword>`
If ``true``, a space will be inserted after the 'template' keyword.
If ``true``, a space will be inserted after the ``template`` keyword.

.. code-block:: c++

Expand Down Expand Up @@ -5860,7 +5860,7 @@ the configuration (without a prefix: ``Auto``).

* ``SBPO_NonEmptyParentheses`` (in configuration: ``NonEmptyParentheses``)
Put a space before opening parentheses only if the parentheses are not
empty i.e. '()'
empty.

.. code-block:: c++

Expand Down Expand Up @@ -6245,7 +6245,7 @@ the configuration (without a prefix: ``Auto``).
true: false:
x = ( int32 )y vs. x = (int32)y

* ``bool InEmptyParentheses`` Put a space in parentheses only if the parentheses are empty i.e. '()'
* ``bool InEmptyParentheses`` Insert a space in empty parentheses, i.e. ``()``.

.. code-block:: c++

Expand Down Expand Up @@ -6409,10 +6409,10 @@ the configuration (without a prefix: ``Auto``).
.. code-block:: yaml
TableGenBreakInsideDAGArg: BreakAll
TableGenBreakingDAGArgOperators: ['ins', 'outs']
TableGenBreakingDAGArgOperators: [ins, outs]
makes the line break only occurs inside DAGArgs beginning with the
specified identifiers 'ins' and 'outs'.
specified identifiers ``ins`` and ``outs``.


.. code-block:: c++
Expand Down Expand Up @@ -6450,7 +6450,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
TypenameMacros: ['STACK_OF', 'LIST']
TypenameMacros: [STACK_OF, LIST]
For example: OpenSSL STACK_OF, BSD LIST_ENTRY.

Expand Down Expand Up @@ -6518,7 +6518,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml
WhitespaceSensitiveMacros: ['STRINGIZE', 'PP_STRINGIZE']
WhitespaceSensitiveMacros: [STRINGIZE, PP_STRINGIZE]
For example: BOOST_PP_STRINGIZE

Expand All @@ -6538,7 +6538,7 @@ The goal of the clang-format project is more on the side of supporting a
limited set of styles really well as opposed to supporting every single style
used by a codebase somewhere in the wild. Of course, we do want to support all
major projects and thus have established the following bar for adding style
options. Each new style option must ..
options. Each new style option must:

* be used in a project of significant size (have dozens of contributors)
* have a publicly accessible style guide
Expand Down
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,10 @@ Bug Fixes to C++ Support
- Fix a regression introduced in Clang 18 causing incorrect overload resolution in the presence of functions only
differering by their constraints when only one of these function was variadic.
- Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
- Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
- Fix a crash caused by improper use of ``__array_extent``. (#GH80474)
- Fixed several bugs in capturing variables within unevaluated contexts. (#GH63845), (#GH67260), (#GH69307),
(#GH88081), (#GH89496), (#GH90669) and (#GH91633).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
157 changes: 79 additions & 78 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,41 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C)
strncpy(buf, "a", 1); // warn
}
.. _security-putenv-stack-array:
security.PutenvStackArray (C)
"""""""""""""""""""""""""""""
Finds calls to the ``putenv`` function which pass a pointer to a stack-allocated
(automatic) array as the argument. Function ``putenv`` does not copy the passed
string, only a pointer to the data is stored and this data can be read even by
other threads. Content of a stack-allocated array is likely to be overwritten
after exiting from the function.
The problem can be solved by using a static array variable or dynamically
allocated memory. Even better is to avoid using ``putenv`` (it has other
problems related to memory leaks) and use ``setenv`` instead.
The check corresponds to CERT rule
`POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument
<https://wiki.sei.cmu.edu/confluence/display/c/POS34-C.+Do+not+call+putenv%28%29+with+a+pointer+to+an+automatic+variable+as+the+argument>`_.
.. code-block:: c
int f() {
char env[] = "NAME=value";
return putenv(env); // putenv function should not be called with stack-allocated string
}
There is one case where the checker can report a false positive. This is when
the stack-allocated array is used at `putenv` in a function or code branch that
does not return (process is terminated on all execution paths).
Another special case is if the `putenv` is called from function `main`. Here
the stack is deallocated at the end of the program and it should be no problem
to use the stack-allocated string (a multi-threaded program may require more
attention). The checker does not warn for cases when stack space of `main` is
used at the `putenv` call.
security.SetgidSetuidOrder (C)
""""""""""""""""""""""""""""""
When dropping user-level and group-level privileges in a program by using
Expand Down Expand Up @@ -1235,6 +1270,50 @@ Check calls to various UNIX/Posix functions: ``open, pthread_once, calloc, mallo
.. literalinclude:: checkers/unix_api_example.c
:language: c
.. _unix-BlockInCriticalSection:
unix.BlockInCriticalSection (C, C++)
""""""""""""""""""""""""""""""""""""
Check for calls to blocking functions inside a critical section.
Blocking functions detected by this checker: ``sleep, getc, fgets, read, recv``.
Critical section handling functions modeled by this checker:
``lock, unlock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, mtx_lock, mtx_timedlock, mtx_trylock, mtx_unlock, lock_guard, unique_lock``.
.. code-block:: c
void pthread_lock_example(pthread_mutex_t *m) {
pthread_mutex_lock(m); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
}
.. code-block:: cpp
void overlapping_critical_sections(mtx_t *m1, std::mutex &m2) {
std::lock_guard lg{m2}; // note: entering critical section here
mtx_lock(m1); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
mtx_unlock(m1);
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
// still inside of the critical section of the std::lock_guard
}
**Limitations**
* The ``trylock`` and ``timedlock`` versions of acquiring locks are currently assumed to always succeed.
This can lead to false positives.
.. code-block:: c
void trylock_example(pthread_mutex_t *m) {
if (pthread_mutex_trylock(m) == 0) { // assume trylock always succeeds
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
} else {
sleep(10); // false positive: Incorrect warning about blocking function inside critical section.
}
}
.. _unix-Errno:
unix.Errno (C)
Expand Down Expand Up @@ -2833,41 +2912,6 @@ Warn on mmap() calls that are both writable and executable.
// code
}
.. _alpha-security-putenv-stack-array:
alpha.security.PutenvStackArray (C)
"""""""""""""""""""""""""""""""""""
Finds calls to the ``putenv`` function which pass a pointer to a stack-allocated
(automatic) array as the argument. Function ``putenv`` does not copy the passed
string, only a pointer to the data is stored and this data can be read even by
other threads. Content of a stack-allocated array is likely to be overwritten
after returning from the parent function.
The problem can be solved by using a static array variable or dynamically
allocated memory. Even better is to avoid using ``putenv`` (it has other
problems related to memory leaks) and use ``setenv`` instead.
The check corresponds to CERT rule
`POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument
<https://wiki.sei.cmu.edu/confluence/display/c/POS34-C.+Do+not+call+putenv%28%29+with+a+pointer+to+an+automatic+variable+as+the+argument>`_.
.. code-block:: c
int f() {
char env[] = "NAME=value";
return putenv(env); // putenv function should not be called with stack-allocated string
}
There is one case where the checker can report a false positive. This is when
the stack-allocated array is used at `putenv` in a function or code branch that
does not return (calls `fork` or `exec` like function).
Another special case is if the `putenv` is called from function `main`. Here
the stack is deallocated at the end of the program and it should be no problem
to use the stack-allocated string (a multi-threaded program may require more
attention). The checker does not warn for cases when stack space of `main` is
used at the `putenv` call.
.. _alpha-security-ReturnPtrRange:
alpha.security.ReturnPtrRange (C)
Expand Down Expand Up @@ -3130,49 +3174,6 @@ For a more detailed description of configuration options, please see the
alpha.unix
^^^^^^^^^^
.. _alpha-unix-BlockInCriticalSection:
alpha.unix.BlockInCriticalSection (C)
"""""""""""""""""""""""""""""""""""""
Check for calls to blocking functions inside a critical section.
Blocking functions detected by this checker: ``sleep, getc, fgets, read, recv``.
Critical section handling functions modelled by this checker: ``lock, unlock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, mtx_lock, mtx_timedlock, mtx_trylock, mtx_unlock, lock_guard, unique_lock``.
.. code-block:: c
void pthread_lock_example(pthread_mutex_t *m) {
pthread_mutex_lock(m); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
}
.. code-block:: cpp
void overlapping_critical_sections(mtx_t *m1, std::mutex &m2) {
std::lock_guard lg{m2}; // note: entering critical section here
mtx_lock(m1); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
mtx_unlock(m1);
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
// still inside of the critical section of the std::lock_guard
}
**Limitations**
* The ``trylock`` and ``timedlock`` versions of acquiring locks are currently assumed to always succeed.
This can lead to false positives.
.. code-block:: c
void trylock_example(pthread_mutex_t *m) {
if (pthread_mutex_trylock(m) == 0) { // assume trylock always succeeds
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
} else {
sleep(10); // false positive: Incorrect warning about blocking function inside critical section.
}
}
.. _alpha-unix-Chroot:
alpha.unix.Chroot (C)
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/APINotes/APINotesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H

#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
Expand All @@ -24,6 +23,7 @@ namespace clang {
class DirectoryEntry;
class FileEntry;
class LangOptions;
class Module;
class SourceManager;

namespace api_notes {
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,13 @@ class alignas(8) Decl {
/// Whether this declaration comes from another module unit.
bool isInAnotherModuleUnit() const;

/// Whether the definition of the declaration should be emitted in external
/// sources.
bool shouldEmitInExternalSource() const;

/// Whether this declaration comes from a named module;
bool isInNamedModule() const;

/// Whether this declaration comes from explicit global module.
bool isFromExplicitGlobalModule() const;

Expand Down Expand Up @@ -2148,6 +2155,10 @@ class DeclContext {
getDeclKind() <= Decl::lastRecord;
}

bool isRequiresExprBody() const {
return getDeclKind() == Decl::RequiresExprBody;
}

bool isNamespace() const { return getDeclKind() == Decl::Namespace; }

bool isStdNamespace() const;
Expand Down
5 changes: 0 additions & 5 deletions clang/include/clang/AST/TemplateName.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,6 @@ class TemplateName {

TemplateName getUnderlying() const;

/// Get the template name to substitute when this template name is used as a
/// template template argument. This refers to the most recent declaration of
/// the template, including any default template arguments.
TemplateName getNameToSubstitute() const;

TemplateNameDependence getDependence() const;

/// Determines whether this is a dependent template name.
Expand Down
47 changes: 47 additions & 0 deletions clang/include/clang/Analysis/FlowSensitive/ASTOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "llvm/ADT/DenseSet.h"
Expand Down Expand Up @@ -80,6 +81,52 @@ class RecordInitListHelper {
std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
};

/// Specialization of `RecursiveASTVisitor` that visits those nodes that are
/// relevant to the dataflow analysis; generally, these are the ones that also
/// appear in the CFG.
/// To start the traversal, call `TraverseStmt()` on the statement or body of
/// the function to analyze. Don't call `TraverseDecl()` on the function itself;
/// this won't work as `TraverseDecl()` contains code to avoid traversing nested
/// functions.
template <class Derived>
class AnalysisASTVisitor : public RecursiveASTVisitor<Derived> {
public:
bool shouldVisitImplicitCode() { return true; }

bool shouldVisitLambdaBody() const { return false; }

bool TraverseDecl(Decl *D) {
// Don't traverse nested record or function declarations.
// - We won't be analyzing code contained in these anyway
// - We don't model fields that are used only in these nested declaration,
// so trying to propagate a result object to initializers of such fields
// would cause an error.
if (isa_and_nonnull<RecordDecl>(D) || isa_and_nonnull<FunctionDecl>(D))
return true;

return RecursiveASTVisitor<Derived>::TraverseDecl(D);
}

// Don't traverse expressions in unevaluated contexts, as we don't model
// fields that are only used in these.
// Note: The operand of the `noexcept` operator is an unevaluated operand, but
// nevertheless it appears in the Clang CFG, so we don't exclude it here.
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
return true;
}

bool TraverseBindingDecl(BindingDecl *BD) {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
if (VarDecl *HoldingVar = BD->getHoldingVar())
TraverseDecl(HoldingVar);
return RecursiveASTVisitor<Derived>::TraverseBindingDecl(BD);
}
};

/// A collection of several types of declarations, all referenced from the same
/// function.
struct ReferencedDecls {
Expand Down
52 changes: 52 additions & 0 deletions clang/include/clang/Basic/ASTSourceDescriptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===- ASTSourceDescriptor.h -----------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
/// \file
/// Defines the clang::ASTSourceDescriptor class, which abstracts clang modules
/// and precompiled header files
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_BASIC_ASTSOURCEDESCRIPTOR_H
#define LLVM_CLANG_BASIC_ASTSOURCEDESCRIPTOR_H

#include "clang/Basic/Module.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <utility>

namespace clang {

/// Abstracts clang modules and precompiled header files and holds
/// everything needed to generate debug info for an imported module
/// or PCH.
class ASTSourceDescriptor {
StringRef PCHModuleName;
StringRef Path;
StringRef ASTFile;
ASTFileSignature Signature;
Module *ClangModule = nullptr;

public:
ASTSourceDescriptor() = default;
ASTSourceDescriptor(StringRef Name, StringRef Path, StringRef ASTFile,
ASTFileSignature Signature)
: PCHModuleName(std::move(Name)), Path(std::move(Path)),
ASTFile(std::move(ASTFile)), Signature(Signature) {}
ASTSourceDescriptor(Module &M);

std::string getModuleName() const;
StringRef getPath() const { return Path; }
StringRef getASTFile() const { return ASTFile; }
ASTFileSignature getSignature() const { return Signature; }
Module *getModuleOrNull() const { return ClangModule; }
};

} // namespace clang

#endif // LLVM_CLANG_BASIC_ASTSOURCEDESCRIPTOR_H
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3068,7 +3068,8 @@ def M68kRTD: DeclOrTypeAttr {
let Documentation = [M68kRTDDocs];
}

def PreserveNone : DeclOrTypeAttr, TargetSpecificAttr<TargetAnyX86> {
def PreserveNone : DeclOrTypeAttr,
TargetSpecificAttr<TargetArch<!listconcat(TargetAArch64.Arches, TargetAnyX86.Arches)>> {
let Spellings = [Clang<"preserve_none">];
let Subjects = SubjectList<[FunctionLike]>;
let Documentation = [PreserveNoneDocs];
Expand Down Expand Up @@ -4499,6 +4500,7 @@ def HLSLShader : InheritableAttr {
case HLSLShaderAttr::Mesh: return llvm::Triple::Mesh;
case HLSLShaderAttr::Amplification: return llvm::Triple::Amplification;
}
llvm_unreachable("unknown enumeration value");
}
}];
}
Expand Down
17 changes: 10 additions & 7 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -5660,18 +5660,21 @@ experimental at this time.
def PreserveNoneDocs : Documentation {
let Category = DocCatCallingConvs;
let Content = [{
On X86-64 target, this attribute changes the calling convention of a function.
On X86-64 and AArch64 targets, this attribute changes the calling convention of a function.
The ``preserve_none`` calling convention tries to preserve as few general
registers as possible. So all general registers are caller saved registers. It
also uses more general registers to pass arguments. This attribute doesn't
impact floating-point registers (XMMs/YMMs). Floating-point registers still
follow the c calling convention. ``preserve_none``'s ABI is still unstable, and
impact floating-point registers. ``preserve_none``'s ABI is still unstable, and
may be changed in the future.

- Only RSP and RBP are preserved by callee.

- Register R12, R13, R14, R15, RDI, RSI, RDX, RCX, R8, R9, R11, and RAX now can
be used to pass function arguments.
- On X86-64, only RSP and RBP are preserved by the callee.
Registers R12, R13, R14, R15, RDI, RSI, RDX, RCX, R8, R9, R11, and RAX now can
be used to pass function arguments. Floating-point registers (XMMs/YMMs) still
follow the C calling convention.
- On AArch64, only LR and FP are preserved by the callee.
Registers X19-X28, X0-X7, and X9-X15 are used to pass function arguments.
X8, X16-X18, SIMD and floating-point registers follow the AAPCS calling
convention.
}];
}

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/CharInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ LLVM_READONLY inline bool isRawStringDelimBody(unsigned char c) {
using namespace charinfo;
return (InfoTable[c] & (CHAR_UPPER | CHAR_LOWER | CHAR_PERIOD | CHAR_DIGIT |
CHAR_UNDER | CHAR_PUNCT)) != 0 &&
c != '(' && c != ')';
c != '(' && c != ')' && c != '\\';
}

enum class EscapeChar {
Expand Down
26 changes: 0 additions & 26 deletions clang/include/clang/Basic/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -868,32 +868,6 @@ class VisibleModuleSet {
unsigned Generation = 0;
};

/// Abstracts clang modules and precompiled header files and holds
/// everything needed to generate debug info for an imported module
/// or PCH.
class ASTSourceDescriptor {
StringRef PCHModuleName;
StringRef Path;
StringRef ASTFile;
ASTFileSignature Signature;
Module *ClangModule = nullptr;

public:
ASTSourceDescriptor() = default;
ASTSourceDescriptor(StringRef Name, StringRef Path, StringRef ASTFile,
ASTFileSignature Signature)
: PCHModuleName(std::move(Name)), Path(std::move(Path)),
ASTFile(std::move(ASTFile)), Signature(Signature) {}
ASTSourceDescriptor(Module &M);

std::string getModuleName() const;
StringRef getPath() const { return Path; }
StringRef getASTFile() const { return ASTFile; }
ASTFileSignature getSignature() const { return Signature; }
Module *getModuleOrNull() const { return ClangModule; }
};


} // namespace clang

#endif // LLVM_CLANG_BASIC_MODULE_H
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H
#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H

#include "clang/Basic/Module.h"
#include "clang/ExtractAPI/API.h"
#include "clang/ExtractAPI/APIIgnoresList.h"
#include "clang/ExtractAPI/Serialization/APISetVisitor.h"
Expand Down
68 changes: 34 additions & 34 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,8 @@ struct FormatStyle {
enum ShortFunctionStyle : int8_t {
/// Never merge functions into a single line.
SFS_None,
/// Only merge functions defined inside a class. Same as "inline",
/// except it does not implies "empty": i.e. top level empty functions
/// Only merge functions defined inside a class. Same as ``inline``,
/// except it does not implies ``empty``: i.e. top level empty functions
/// are not merged either.
/// \code
/// class Foo {
Expand All @@ -836,7 +836,7 @@ struct FormatStyle {
/// }
/// \endcode
SFS_Empty,
/// Only merge functions defined inside a class. Implies "empty".
/// Only merge functions defined inside a class. Implies ``empty``.
/// \code
/// class Foo {
/// void f() { foo(); }
Expand Down Expand Up @@ -1167,7 +1167,7 @@ struct FormatStyle {
///
/// In the .clang-format configuration file, this can be configured like:
/// \code{.yaml}
/// AttributeMacros: ['__capability', '__output', '__unused']
/// AttributeMacros: [__capability, __output, __unused]
/// \endcode
///
/// \version 12
Expand Down Expand Up @@ -2631,7 +2631,7 @@ struct FormatStyle {
///
/// In the .clang-format configuration file, this can be configured like:
/// \code{.yaml}
/// ForEachMacros: ['RANGES_FOR', 'FOREACH']
/// ForEachMacros: [RANGES_FOR, FOREACH]
/// \endcode
///
/// For example: BOOST_FOREACH.
Expand All @@ -2653,7 +2653,7 @@ struct FormatStyle {
///
/// In the .clang-format configuration file, this can be configured like:
/// \code{.yaml}
/// IfMacros: ['IF']
/// IfMacros: [IF]
/// \endcode
///
/// For example: `KJ_IF_MAYBE
Expand Down Expand Up @@ -3030,7 +3030,7 @@ struct FormatStyle {
/// in the following yaml example. This will result in imports being
/// formatted as in the Java example below.
/// \code{.yaml}
/// JavaImportGroups: ['com.example', 'com', 'org']
/// JavaImportGroups: [com.example, com, org]
/// \endcode
///
/// \code{.java}
Expand Down Expand Up @@ -3086,7 +3086,7 @@ struct FormatStyle {
/// VeryLongImportsAreAnnoying,
/// VeryLongImportsAreAnnoying,
/// VeryLongImportsAreAnnoying,
/// } from 'some/module.js'
/// } from "some/module.js"
///
/// false:
/// import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js"
Expand Down Expand Up @@ -3615,7 +3615,7 @@ struct FormatStyle {
/// Change specifiers/qualifiers to be aligned based on ``QualifierOrder``.
/// With:
/// \code{.yaml}
/// QualifierOrder: ['inline', 'static', 'type', 'const']
/// QualifierOrder: [inline, static, type, const]
/// \endcode
///
/// \code
Expand Down Expand Up @@ -3650,15 +3650,15 @@ struct FormatStyle {
/// * type
///
/// \note
/// it MUST contain 'type'.
/// It **must** contain ``type``.
/// \endnote
///
/// Items to the left of 'type' will be placed to the left of the type and
/// aligned in the order supplied. Items to the right of 'type' will be
/// Items to the left of ``type`` will be placed to the left of the type and
/// aligned in the order supplied. Items to the right of ``type`` will be
/// placed to the right of the type and aligned in the order supplied.
///
/// \code{.yaml}
/// QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ]
/// QualifierOrder: [inline, static, type, const, volatile]
/// \endcode
/// \version 14
std::vector<std::string> QualifierOrder;
Expand Down Expand Up @@ -3692,10 +3692,10 @@ struct FormatStyle {
/// name will be reformatted assuming the specified language based on the
/// style for that language defined in the .clang-format file. If no style has
/// been defined in the .clang-format file for the specific language, a
/// predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not
/// found, the formatting is based on llvm style. A matching delimiter takes
/// precedence over a matching enclosing function name for determining the
/// language of the raw string contents.
/// predefined style given by ``BasedOnStyle`` is used. If ``BasedOnStyle`` is
/// not found, the formatting is based on ``LLVM`` style. A matching delimiter
/// takes precedence over a matching enclosing function name for determining
/// the language of the raw string contents.
///
/// If a canonical delimiter is specified, occurrences of other delimiters for
/// the same language will be updated to the canonical if possible.
Expand All @@ -3708,17 +3708,17 @@ struct FormatStyle {
/// RawStringFormats:
/// - Language: TextProto
/// Delimiters:
/// - 'pb'
/// - 'proto'
/// - pb
/// - proto
/// EnclosingFunctions:
/// - 'PARSE_TEXT_PROTO'
/// - PARSE_TEXT_PROTO
/// BasedOnStyle: google
/// - Language: Cpp
/// Delimiters:
/// - 'cc'
/// - 'cpp'
/// BasedOnStyle: llvm
/// CanonicalDelimiter: 'cc'
/// - cc
/// - cpp
/// BasedOnStyle: LLVM
/// CanonicalDelimiter: cc
/// \endcode
/// \version 6
std::vector<RawStringFormat> RawStringFormats;
Expand Down Expand Up @@ -4046,7 +4046,7 @@ struct FormatStyle {
///
/// This determines the maximum length of short namespaces by counting
/// unwrapped lines (i.e. containing neither opening nor closing
/// namespace brace) and makes "FixNamespaceComments" omit adding
/// namespace brace) and makes ``FixNamespaceComments`` omit adding
/// end comments for those.
/// \code
/// ShortNamespaceLines: 1 vs. ShortNamespaceLines: 0
Expand Down Expand Up @@ -4138,7 +4138,7 @@ struct FormatStyle {
/// \endcode
SUD_Never,
/// Using declarations are sorted in the order defined as follows:
/// Split the strings by "::" and discard any initial empty strings. Sort
/// Split the strings by ``::`` and discard any initial empty strings. Sort
/// the lists of names lexicographically, and within those groups, names are
/// in case-insensitive lexicographic order.
/// \code
Expand All @@ -4150,7 +4150,7 @@ struct FormatStyle {
/// \endcode
SUD_Lexicographic,
/// Using declarations are sorted in the order defined as follows:
/// Split the strings by "::" and discard any initial empty strings. The
/// Split the strings by ``::`` and discard any initial empty strings. The
/// last element of each list is a non-namespace name; all others are
/// namespace names. Sort the lists of names lexicographically, where the
/// sort order of individual names is that all non-namespace names come
Expand Down Expand Up @@ -4186,7 +4186,7 @@ struct FormatStyle {
/// \version 9
bool SpaceAfterLogicalNot;

/// If \c true, a space will be inserted after the 'template' keyword.
/// If \c true, a space will be inserted after the ``template`` keyword.
/// \code
/// true: false:
/// template <int> void foo(); vs. template<int> void foo();
Expand Down Expand Up @@ -4316,7 +4316,7 @@ struct FormatStyle {
/// \endcode
SBPO_ControlStatementsExceptControlMacros,
/// Put a space before opening parentheses only if the parentheses are not
/// empty i.e. '()'
/// empty.
/// \code
/// void() {
/// if (true) {
Expand Down Expand Up @@ -4668,7 +4668,7 @@ struct FormatStyle {
/// x = ( int32 )y vs. x = (int32)y
/// \endcode
bool InCStyleCasts;
/// Put a space in parentheses only if the parentheses are empty i.e. '()'
/// Insert a space in empty parentheses, i.e. ``()``.
/// \code
/// true: false:
/// void f( ) { vs. void f() {
Expand Down Expand Up @@ -4804,11 +4804,11 @@ struct FormatStyle {
/// For example the configuration,
/// \code{.yaml}
/// TableGenBreakInsideDAGArg: BreakAll
/// TableGenBreakingDAGArgOperators: ['ins', 'outs']
/// TableGenBreakingDAGArgOperators: [ins, outs]
/// \endcode
///
/// makes the line break only occurs inside DAGArgs beginning with the
/// specified identifiers 'ins' and 'outs'.
/// specified identifiers ``ins`` and ``outs``.
///
/// \code
/// let DAGArgIns = (ins
Expand Down Expand Up @@ -4873,7 +4873,7 @@ struct FormatStyle {
///
/// In the .clang-format configuration file, this can be configured like:
/// \code{.yaml}
/// TypenameMacros: ['STACK_OF', 'LIST']
/// TypenameMacros: [STACK_OF, LIST]
/// \endcode
///
/// For example: OpenSSL STACK_OF, BSD LIST_ENTRY.
Expand Down Expand Up @@ -4929,7 +4929,7 @@ struct FormatStyle {
///
/// In the .clang-format configuration file, this can be configured like:
/// \code{.yaml}
/// WhitespaceSensitiveMacros: ['STRINGIZE', 'PP_STRINGIZE']
/// WhitespaceSensitiveMacros: [STRINGIZE, PP_STRINGIZE]
/// \endcode
///
/// For example: BOOST_PP_STRINGIZE
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Lex/DependencyDirectivesScanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H
#define LLVM_CLANG_LEX_DEPENDENCYDIRECTIVESSCANNER_H

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"

Expand Down Expand Up @@ -117,7 +118,7 @@ struct Directive {
bool scanSourceForDependencyDirectives(
StringRef Input, SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
SmallVectorImpl<dependency_directives_scan::Directive> &Directives,
DiagnosticsEngine *Diags = nullptr,
const LangOptions &LangOpts, DiagnosticsEngine *Diags = nullptr,
SourceLocation InputSourceLoc = SourceLocation());

/// Print the previously scanned dependency directives as minimized source text.
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ class ASTReader
FileManager &FileMgr;
const PCHContainerReader &PCHContainerRdr;
DiagnosticsEngine &Diags;
// Sema has duplicate logic, but SemaObj can sometimes be null so ASTReader
// has its own version.
bool WarnedStackExhausted = false;

/// The semantic analysis object that will be processing the
/// AST files and the translation unit that uses it.
Expand Down Expand Up @@ -2135,6 +2138,8 @@ class ASTReader
/// Report a diagnostic.
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) const;

void warnStackExhausted(SourceLocation Loc);

IdentifierInfo *DecodeIdentifierInfo(serialization::IdentifierID ID);

IdentifierInfo *readIdentifier(ModuleFile &M, const RecordData &Record,
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaConsumer.h"
Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Serialization/ModuleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#define LLVM_CLANG_SERIALIZATION_MODULEMANAGER_H

#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Serialization/ModuleFile.h"
#include "llvm/ADT/DenseMap.h"
Expand Down
18 changes: 9 additions & 9 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ def UnixAPIMisuseChecker : Checker<"API">,
HelpText<"Check calls to various UNIX/Posix functions">,
Documentation<HasDocumentation>;

def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
HelpText<"Check for calls to blocking functions inside a critical section">,
Documentation<HasDocumentation>;

def DynamicMemoryModeling: Checker<"DynamicMemoryModeling">,
HelpText<"The base of several malloc() related checkers. On it's own it "
"emits no reports, but adds valuable information to the analysis "
Expand Down Expand Up @@ -619,10 +623,6 @@ def SimpleStreamChecker : Checker<"SimpleStream">,
HelpText<"Check for misuses of stream APIs">,
Documentation<HasDocumentation>;

def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
HelpText<"Check for calls to blocking functions inside a critical section">,
Documentation<HasDocumentation>;

} // end "alpha.unix"

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1011,6 +1011,11 @@ def FloatLoopCounter : Checker<"FloatLoopCounter">,
Dependencies<[SecuritySyntaxChecker]>,
Documentation<HasDocumentation>;

def PutenvStackArray : Checker<"PutenvStackArray">,
HelpText<"Finds calls to the function 'putenv' which pass a pointer to "
"an automatic (stack-allocated) array as the argument.">,
Documentation<HasDocumentation>;

def SetgidSetuidOrderChecker : Checker<"SetgidSetuidOrder">,
HelpText<"Warn on possible reversed order of 'setgid(getgid()))' and "
"'setuid(getuid())' (CERT: POS36-C)">,
Expand Down Expand Up @@ -1065,11 +1070,6 @@ def MmapWriteExecChecker : Checker<"MmapWriteExec">,
]>,
Documentation<HasDocumentation>;

def PutenvStackArray : Checker<"PutenvStackArray">,
HelpText<"Finds calls to the function 'putenv' which pass a pointer to "
"an automatic (stack-allocated) array as the argument.">,
Documentation<HasDocumentation>;

def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,
HelpText<"Check for an out-of-bound pointer being returned to callers">,
Documentation<HasDocumentation>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ class DependencyScanningWorkerFilesystem
///
/// Returns true if the directive tokens are populated for this file entry,
/// false if not (i.e. this entry is not a file or its scan fails).
bool ensureDirectiveTokensArePopulated(EntryRef Entry);
bool ensureDirectiveTokensArePopulated(EntryRef Entry,
const LangOptions &LangOpts);

/// Check whether \p Path exists. By default checks cached result of \c
/// status(), and falls back on FS if unable to do so.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_MODULEDEPCOLLECTOR_H

#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/Utils.h"
Expand Down Expand Up @@ -138,6 +139,10 @@ struct ModuleDeps {
/// determined that the differences are benign for this compilation.
std::vector<ModuleID> ClangModuleDeps;

/// The set of libraries or frameworks to link against when
/// an entity from this module is used.
llvm::SmallVector<Module::LinkLibrary, 2> LinkLibraries;

/// Get (or compute) the compiler invocation that can be used to build this
/// module. Does not include argv[0].
const std::vector<std::string> &getBuildArguments();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/APINotes/APINotesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceMgrAdapter.h"
#include "clang/Basic/Version.h"
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12018,7 +12018,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return false;

// Variables in other module units shouldn't be forced to be emitted.
if (VD->isInAnotherModuleUnit())
if (VD->shouldEmitInExternalSource())
return false;

// Variables that can be needed in other TUs are required.
Expand Down
1 change: 0 additions & 1 deletion clang/lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include "clang/AST/DeclLookups.h"
#include "clang/AST/JSONNodeDumper.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/raw_ostream.h"

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2929,7 +2929,7 @@ ExpectedDecl ASTNodeImporter::VisitEnumDecl(EnumDecl *D) {

// We may already have an enum of the same name; try to find and match it.
EnumDecl *PrevDecl = nullptr;
if (!DC->isFunctionOrMethod() && SearchName) {
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
auto FoundDecls =
Importer.findDeclsInToCtx(DC, SearchName);
Expand Down
11 changes: 2 additions & 9 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1174,13 +1174,6 @@ Linkage NamedDecl::getLinkageInternal() const {
.getLinkage();
}

/// Determine whether D is attached to a named module.
static bool isInNamedModule(const NamedDecl *D) {
if (auto *M = D->getOwningModule())
return M->isNamedModule();
return false;
}

static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
// FIXME: Handle isModulePrivate.
switch (D->getModuleOwnershipKind()) {
Expand All @@ -1190,7 +1183,7 @@ static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
return false;
case Decl::ModuleOwnershipKind::Visible:
case Decl::ModuleOwnershipKind::VisibleWhenImported:
return isInNamedModule(D);
return D->isInNamedModule();
}
llvm_unreachable("unexpected module ownership kind");
}
Expand All @@ -1208,7 +1201,7 @@ Linkage NamedDecl::getFormalLinkage() const {
// [basic.namespace.general]/p2
// A namespace is never attached to a named module and never has a name with
// module linkage.
if (isInNamedModule(this) && InternalLinkage == Linkage::External &&
if (isInNamedModule() && InternalLinkage == Linkage::External &&
!isExportedFromModuleInterfaceUnit(
cast<NamedDecl>(this->getCanonicalDecl())) &&
!isa<NamespaceDecl>(this))
Expand Down
23 changes: 11 additions & 12 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1100,23 +1100,22 @@ bool Decl::isInExportDeclContext() const {
bool Decl::isInAnotherModuleUnit() const {
auto *M = getOwningModule();

if (!M)
if (!M || !M->isNamedModule())
return false;

M = M->getTopLevelModule();
// FIXME: It is problematic if the header module lives in another module
// unit. Consider to fix this by techniques like
// ExternalASTSource::hasExternalDefinitions.
if (M->isHeaderLikeModule())
return false;
return M != getASTContext().getCurrentNamedModule();
}

// A global module without parent implies that we're parsing the global
// module. So it can't be in another module unit.
if (M->isGlobalModule())
bool Decl::shouldEmitInExternalSource() const {
ExternalASTSource *Source = getASTContext().getExternalSource();
if (!Source)
return false;

assert(M->isNamedModule() && "New module kind?");
return M != getASTContext().getCurrentNamedModule();
return Source->hasExternalDefinitions(this) == ExternalASTSource::EK_Always;
}

bool Decl::isInNamedModule() const {
return getOwningModule() && getOwningModule()->isNamedModule();
}

bool Decl::isFromExplicitGlobalModule() const {
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3618,12 +3618,10 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ConceptSpecializationExprClass:
case RequiresExprClass:
case SYCLUniqueStableNameExprClass:
case PackIndexingExprClass:
// These never have a side-effect.
return false;

case PackIndexingExprClass:
return cast<PackIndexingExpr>(this)->getSelectedExpr()->HasSideEffects(
Ctx, IncludePossibleEffects);
case ConstantExprClass:
// FIXME: Move this into the "return false;" block above.
return cast<ConstantExpr>(this)->getSubExpr()->HasSideEffects(
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ExternalASTSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Basic/ASTSourceDescriptor.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/ErrorHandling.h"
#include <cstdint>
Expand Down
17 changes: 0 additions & 17 deletions clang/lib/AST/TemplateName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,23 +214,6 @@ UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const {
return nullptr;
}

TemplateName TemplateName::getNameToSubstitute() const {
TemplateDecl *Decl = getAsTemplateDecl();

// Substituting a dependent template name: preserve it as written.
if (!Decl)
return *this;

// If we have a template declaration, use the most recent non-friend
// declaration of that template.
Decl = cast<TemplateDecl>(Decl->getMostRecentDecl());
while (Decl->getFriendObjectKind()) {
Decl = cast<TemplateDecl>(Decl->getPreviousDecl());
assert(Decl && "all declarations of template are friends");
}
return TemplateName(Decl);
}

TemplateNameDependence TemplateName::getDependence() const {
auto D = TemplateNameDependence::None;
switch (getKind()) {
Expand Down
114 changes: 60 additions & 54 deletions clang/lib/Analysis/FlowSensitive/ASTOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,90 +188,96 @@ static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
return nullptr;
}

static void getReferencedDecls(const Decl &D, ReferencedDecls &Referenced) {
insertIfGlobal(D, Referenced.Globals);
insertIfFunction(D, Referenced.Functions);
if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
for (const auto *B : Decomp->bindings())
if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
// FIXME: should we be using `E->getFoundDecl()`?
if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
Referenced.Fields.insert(FD);
}
class ReferencedDeclsVisitor
: public AnalysisASTVisitor<ReferencedDeclsVisitor> {
public:
ReferencedDeclsVisitor(ReferencedDecls &Referenced)
: Referenced(Referenced) {}

void TraverseConstructorInits(const CXXConstructorDecl *Ctor) {
for (const CXXCtorInitializer *Init : Ctor->inits()) {
if (Init->isMemberInitializer()) {
Referenced.Fields.insert(Init->getMember());
} else if (Init->isIndirectMemberInitializer()) {
for (const auto *I : Init->getIndirectMember()->chain())
Referenced.Fields.insert(cast<FieldDecl>(I));
}

Expr *InitExpr = Init->getInit();

// Also collect declarations referenced in `InitExpr`.
TraverseStmt(InitExpr);

/// Traverses `S` and inserts into `Referenced` any declarations that are
/// declared in or referenced from sub-statements.
static void getReferencedDecls(const Stmt &S, ReferencedDecls &Referenced) {
for (auto *Child : S.children())
if (Child != nullptr)
getReferencedDecls(*Child, Referenced);
if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S))
getReferencedDecls(*DefaultArg->getExpr(), Referenced);
if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S))
getReferencedDecls(*DefaultInit->getExpr(), Referenced);

if (auto *DS = dyn_cast<DeclStmt>(&S)) {
if (DS->isSingleDecl())
getReferencedDecls(*DS->getSingleDecl(), Referenced);
else
for (auto *D : DS->getDeclGroup())
getReferencedDecls(*D, Referenced);
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
// If this is a `CXXDefaultInitExpr`, also collect declarations referenced
// within the default expression.
if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))
TraverseStmt(DefaultInit->getExpr());
}
}

bool VisitDecl(Decl *D) {
insertIfGlobal(*D, Referenced.Globals);
insertIfFunction(*D, Referenced.Functions);
return true;
}

bool VisitDeclRefExpr(DeclRefExpr *E) {
insertIfGlobal(*E->getDecl(), Referenced.Globals);
insertIfFunction(*E->getDecl(), Referenced.Functions);
} else if (const auto *C = dyn_cast<CXXMemberCallExpr>(&S)) {
return true;
}

bool VisitCXXMemberCallExpr(CXXMemberCallExpr *C) {
// If this is a method that returns a member variable but does nothing else,
// model the field of the return value.
if (MemberExpr *E = getMemberForAccessor(*C))
if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))
Referenced.Fields.insert(FD);
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
return true;
}

bool VisitMemberExpr(MemberExpr *E) {
// FIXME: should we be using `E->getFoundDecl()`?
const ValueDecl *VD = E->getMemberDecl();
insertIfGlobal(*VD, Referenced.Globals);
insertIfFunction(*VD, Referenced.Functions);
if (const auto *FD = dyn_cast<FieldDecl>(VD))
Referenced.Fields.insert(FD);
} else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
return true;
}

bool VisitInitListExpr(InitListExpr *InitList) {
if (InitList->getType()->isRecordType())
for (const auto *FD : getFieldsForInitListExpr(InitList))
Referenced.Fields.insert(FD);
} else if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(&S)) {
return true;
}

bool VisitCXXParenListInitExpr(CXXParenListInitExpr *ParenInitList) {
if (ParenInitList->getType()->isRecordType())
for (const auto *FD : getFieldsForInitListExpr(ParenInitList))
Referenced.Fields.insert(FD);
return true;
}
}

private:
ReferencedDecls &Referenced;
};

ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
ReferencedDecls Result;
// Look for global variable and field references in the
// constructor-initializers.
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD)) {
for (const auto *Init : CtorDecl->inits()) {
if (Init->isMemberInitializer()) {
Result.Fields.insert(Init->getMember());
} else if (Init->isIndirectMemberInitializer()) {
for (const auto *I : Init->getIndirectMember()->chain())
Result.Fields.insert(cast<FieldDecl>(I));
}
const Expr *E = Init->getInit();
assert(E != nullptr);
getReferencedDecls(*E, Result);
}
// Add all fields mentioned in default member initializers.
for (const FieldDecl *F : CtorDecl->getParent()->fields())
if (const auto *I = F->getInClassInitializer())
getReferencedDecls(*I, Result);
}
getReferencedDecls(*FD.getBody(), Result);
ReferencedDeclsVisitor Visitor(Result);
Visitor.TraverseStmt(FD.getBody());
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
Visitor.TraverseConstructorInits(CtorDecl);

return Result;
}

ReferencedDecls getReferencedDecls(const Stmt &S) {
ReferencedDecls Result;
getReferencedDecls(S, Result);
ReferencedDeclsVisitor Visitor(Result);
Visitor.TraverseStmt(const_cast<Stmt *>(&S));
return Result;
}

Expand Down
37 changes: 1 addition & 36 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ namespace {
// Visitor that builds a map from record prvalues to result objects.
// For each result object that it encounters, it propagates the storage location
// of the result object to all record prvalues that can initialize it.
class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
class ResultObjectVisitor : public AnalysisASTVisitor<ResultObjectVisitor> {
public:
// `ResultObjectMap` will be filled with a map from record prvalues to result
// object. If this visitor will traverse a function that returns a record by
Expand All @@ -310,10 +310,6 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
: ResultObjectMap(ResultObjectMap),
LocForRecordReturnVal(LocForRecordReturnVal), DACtx(DACtx) {}

bool shouldVisitImplicitCode() { return true; }

bool shouldVisitLambdaBody() const { return false; }

// Traverse all member and base initializers of `Ctor`. This function is not
// called by `RecursiveASTVisitor`; it should be called manually if we are
// analyzing a constructor. `ThisPointeeLoc` is the storage location that
Expand Down Expand Up @@ -342,37 +338,6 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
}
}

bool TraverseDecl(Decl *D) {
// Don't traverse nested record or function declarations.
// - We won't be analyzing code contained in these anyway
// - We don't model fields that are used only in these nested declaration,
// so trying to propagate a result object to initializers of such fields
// would cause an error.
if (isa_and_nonnull<RecordDecl>(D) || isa_and_nonnull<FunctionDecl>(D))
return true;

return RecursiveASTVisitor<ResultObjectVisitor>::TraverseDecl(D);
}

// Don't traverse expressions in unevaluated contexts, as we don't model
// fields that are only used in these.
// Note: The operand of the `noexcept` operator is an unevaluated operand, but
// nevertheless it appears in the Clang CFG, so we don't exclude it here.
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
return true;
}

bool TraverseBindingDecl(BindingDecl *BD) {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
if (VarDecl *HoldingVar = BD->getHoldingVar())
TraverseDecl(HoldingVar);
return RecursiveASTVisitor<ResultObjectVisitor>::TraverseBindingDecl(BD);
}

bool VisitVarDecl(VarDecl *VD) {
if (VD->getType()->isRecordType() && VD->hasInit())
PropagateResultObject(
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
const Expr *RHS = S->getRHS();
assert(RHS != nullptr);

// Do compound assignments up-front, as there are so many of them and we
// don't want to list all of them in the switch statement below.
// To avoid generating unnecessary values, we don't create a new value but
// instead leave it to the specific analysis to do this if desired.
if (S->isCompoundAssignmentOp())
propagateStorageLocation(*S->getLHS(), *S, Env);

switch (S->getOpcode()) {
case BO_Assign: {
auto *LHSLoc = Env.getStorageLocation(*LHS);
Expand Down Expand Up @@ -382,6 +389,20 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
Env.setValue(*S, Env.makeNot(*SubExprVal));
break;
}
case UO_PreInc:
case UO_PreDec:
// Propagate the storage location, but don't create a new value; to
// avoid generating unnecessary values, we leave it to the specific
// analysis to do this if desired.
propagateStorageLocation(*S->getSubExpr(), *S, Env);
break;
case UO_PostInc:
case UO_PostDec:
// Propagate the old value, but don't create a new value; to avoid
// generating unnecessary values, we leave it to the specific analysis
// to do this if desired.
propagateValue(*S->getSubExpr(), *S, Env);
break;
default:
break;
}
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/Basic/ASTSourceDescriptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===- ASTSourceDescriptor.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
//
//===----------------------------------------------------------------------===//
//
/// Defines the clang::ASTSourceDescriptor class, which abstracts clang modules
/// and precompiled header files
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/ASTSourceDescriptor.h"

namespace clang {

ASTSourceDescriptor::ASTSourceDescriptor(Module &M)
: Signature(M.Signature), ClangModule(&M) {
if (M.Directory)
Path = M.Directory->getName();
if (auto File = M.getASTFile())
ASTFile = File->getName();
}

std::string ASTSourceDescriptor::getModuleName() const {
if (ClangModule)
return ClangModule->Name;
else
return std::string(PCHModuleName);
}

} // namespace clang
1 change: 1 addition & 0 deletions clang/lib/Basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ if(CLANG_VENDOR)
endif()

add_clang_library(clangBasic
ASTSourceDescriptor.cpp
Attributes.cpp
Builtins.cpp
CLWarnings.cpp
Expand Down
15 changes: 0 additions & 15 deletions clang/lib/Basic/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,18 +724,3 @@ void VisibleModuleSet::setVisible(Module *M, SourceLocation Loc,
};
VisitModule({M, nullptr});
}

ASTSourceDescriptor::ASTSourceDescriptor(Module &M)
: Signature(M.Signature), ClangModule(&M) {
if (M.Directory)
Path = M.Directory->getName();
if (auto File = M.getASTFile())
ASTFile = File->getName();
}

std::string ASTSourceDescriptor::getModuleName() const {
if (ClangModule)
return ClangModule->Name;
else
return std::string(PCHModuleName);
}
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,7 @@ AArch64TargetInfo::checkCallingConvention(CallingConv CC) const {
case CC_SwiftAsync:
case CC_PreserveMost:
case CC_PreserveAll:
case CC_PreserveNone:
case CC_OpenCLKernel:
case CC_AArch64VectorCall:
case CC_AArch64SVEPCS:
Expand Down
84 changes: 84 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5737,6 +5737,90 @@ void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var,
Var->addDebugInfo(GVE);
}

void CGDebugInfo::EmitPseudoVariable(CGBuilderTy &Builder,
llvm::Instruction *Value, QualType Ty) {
// Only when -g2 or above is specified, debug info for variables will be
// generated.
if (CGM.getCodeGenOpts().getDebugInfo() <=
llvm::codegenoptions::DebugLineTablesOnly)
return;

llvm::DebugLoc SaveDebugLoc = Builder.getCurrentDebugLocation();
if (!SaveDebugLoc.get())
return;

llvm::DIFile *Unit = SaveDebugLoc->getFile();
llvm::DIType *Type = getOrCreateType(Ty, Unit);

// Check if Value is already a declared variable and has debug info, in this
// case we have nothing to do. Clang emits declared variable as alloca, and
// it is loaded upon use, so we identify such pattern here.
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Value)) {
llvm::Value *Var = Load->getPointerOperand();
if (llvm::Metadata *MDValue = llvm::ValueAsMetadata::getIfExists(Var)) {
if (llvm::Value *DbgValue = llvm::MetadataAsValue::getIfExists(
CGM.getLLVMContext(), MDValue)) {
for (llvm::User *U : DbgValue->users()) {
if (llvm::CallInst *DbgDeclare = dyn_cast<llvm::CallInst>(U)) {
if (DbgDeclare->getCalledFunction()->getIntrinsicID() ==
llvm::Intrinsic::dbg_declare &&
DbgDeclare->getArgOperand(0) == DbgValue) {
// There can be implicit type cast applied on a variable if it is
// an opaque ptr, in this case its debug info may not match the
// actual type of object being used as in the next instruction, so
// we will need to emit a pseudo variable for type-casted value.
llvm::DILocalVariable *MDNode = cast<llvm::DILocalVariable>(
cast<llvm::MetadataAsValue>(DbgDeclare->getOperand(1))
->getMetadata());
if (MDNode->getType() == Type)
return;
}
}
}
}
}
}

// Find the correct location to insert a sequence of instructions to
// materialize Value on the stack.
auto SaveInsertionPoint = Builder.saveIP();
if (llvm::InvokeInst *Invoke = dyn_cast<llvm::InvokeInst>(Value))
Builder.SetInsertPoint(Invoke->getNormalDest()->begin());
else if (llvm::Instruction *Next = Value->getIterator()->getNextNode())
Builder.SetInsertPoint(Next);
else
Builder.SetInsertPoint(Value->getParent());
llvm::DebugLoc DL = Value->getDebugLoc();
if (DL.get())
Builder.SetCurrentDebugLocation(DL);
else if (!Builder.getCurrentDebugLocation().get())
Builder.SetCurrentDebugLocation(SaveDebugLoc);

llvm::AllocaInst *PseudoVar = Builder.CreateAlloca(Value->getType());
Address PseudoVarAddr(PseudoVar, Value->getType(),
CharUnits::fromQuantity(PseudoVar->getAlign()));
llvm::LoadInst *Load = Builder.CreateLoad(PseudoVarAddr);
Value->replaceAllUsesWith(Load);
Builder.SetInsertPoint(Load);
Builder.CreateStore(Value, PseudoVarAddr);

// Emit debug info for materialized Value.
unsigned Line = Builder.getCurrentDebugLocation().getLine();
unsigned Column = Builder.getCurrentDebugLocation().getCol();
llvm::DILocalVariable *D = DBuilder.createAutoVariable(
LexicalBlockStack.back(), "", nullptr, 0, Type, false,
llvm::DINode::FlagArtificial);
llvm::DILocation *DIL =
llvm::DILocation::get(CGM.getLLVMContext(), Line, Column,
LexicalBlockStack.back(), CurInlinedAt);
SmallVector<uint64_t> Expr;
DBuilder.insertDeclare(PseudoVar, D, DBuilder.createExpression(Expr), DIL,
Load);

Builder.restoreIP(SaveInsertionPoint);
Builder.SetCurrentDebugLocation(SaveDebugLoc);
}

void CGDebugInfo::EmitGlobalAlias(const llvm::GlobalValue *GV,
const GlobalDecl GD) {

Expand Down
9 changes: 8 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/ASTSourceDescriptor.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
Expand All @@ -38,6 +38,7 @@ class MDNode;
namespace clang {
class ClassTemplateSpecializationDecl;
class GlobalDecl;
class Module;
class ModuleMap;
class ObjCInterfaceDecl;
class UsingDecl;
Expand Down Expand Up @@ -529,6 +530,12 @@ class CGDebugInfo {
/// Emit information about an external variable.
void EmitExternalVariable(llvm::GlobalVariable *GV, const VarDecl *Decl);

/// Emit a pseudo variable and debug info for an intermediate value if it does
/// not correspond to a variable in the source code, so that a profiler can
/// track more accurate usage of certain instructions of interest.
void EmitPseudoVariable(CGBuilderTy &Builder, llvm::Instruction *Value,
QualType Ty);

/// Emit information about global variable alias.
void EmitGlobalAlias(const llvm::GlobalValue *GV, const GlobalDecl Decl);

Expand Down
21 changes: 20 additions & 1 deletion clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,26 @@ Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
}
}

return EmitLoadOfLValue(E);
llvm::Value *Result = EmitLoadOfLValue(E);

// If -fdebug-info-for-profiling is specified, emit a pseudo variable and its
// debug info for the pointer, even if there is no variable associated with
// the pointer's expression.
if (CGF.CGM.getCodeGenOpts().DebugInfoForProfiling && CGF.getDebugInfo()) {
if (llvm::LoadInst *Load = dyn_cast<llvm::LoadInst>(Result)) {
if (llvm::GetElementPtrInst *GEP =
dyn_cast<llvm::GetElementPtrInst>(Load->getPointerOperand())) {
if (llvm::Instruction *Pointer =
dyn_cast<llvm::Instruction>(GEP->getPointerOperand())) {
QualType Ty = E->getBase()->getType();
if (!E->isArrow())
Ty = CGF.getContext().getPointerType(Ty);
CGF.getDebugInfo()->EmitPseudoVariable(Builder, Pointer, Ty);
}
}
}
}
return Result;
}

Value *ScalarExprEmitter::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,7 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
assert(Def && "The body of the key function is not assigned to Def?");
// If the non-inline key function comes from another module unit, the vtable
// must be defined there.
return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
return Def->shouldEmitInExternalSource() && !Def->isInlineSpecified();
}

/// Given that we're currently at the end of the translation unit, and
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1853,18 +1853,24 @@ static std::string getMangledNameImpl(CodeGenModule &CGM, GlobalDecl GD,
break;
case MultiVersionKind::Target: {
auto *Attr = FD->getAttr<TargetAttr>();
assert(Attr && "Expected TargetAttr to be present "
"for attribute mangling");
const ABIInfo &Info = CGM.getTargetCodeGenInfo().getABIInfo();
Info.appendAttributeMangling(Attr, Out);
break;
}
case MultiVersionKind::TargetVersion: {
auto *Attr = FD->getAttr<TargetVersionAttr>();
assert(Attr && "Expected TargetVersionAttr to be present "
"for attribute mangling");
const ABIInfo &Info = CGM.getTargetCodeGenInfo().getABIInfo();
Info.appendAttributeMangling(Attr, Out);
break;
}
case MultiVersionKind::TargetClones: {
auto *Attr = FD->getAttr<TargetClonesAttr>();
assert(Attr && "Expected TargetClonesAttr to be present "
"for attribute mangling");
unsigned Index = GD.getMultiVersionIndex();
const ABIInfo &Info = CGM.getTargetCodeGenInfo().getABIInfo();
Info.appendAttributeMangling(Attr, Index, Out);
Expand Down
1 change: 0 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include "clang/AST/Mangle.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/NoSanitizeList.h"
#include "clang/Basic/ProfileList.h"
#include "clang/Basic/TargetInfo.h"
Expand Down
Loading