59 changes: 54 additions & 5 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "Function name: " << Function.getPrintName() << "\n");
LLVM_DEBUG(dbgs() << " Address reference: 0x"
<< Twine::utohexstr(Function.getOutputAddress()) << "\n");
LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n", getBFHash(OutputAddress)));
LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n", getBFHash(InputAddress)));
LLVM_DEBUG(dbgs() << " Secondary Entry Points: " << NumSecondaryEntryPoints
<< '\n');

Expand Down Expand Up @@ -197,8 +197,9 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
? SecondaryEntryPointsMap[Address].size()
: 0;
if (Cold) {
size_t HotIndex =
std::distance(ColdPartSource.begin(), ColdPartSource.find(Address));
auto HotEntryIt = Maps.find(ColdPartSource[Address]);
assert(HotEntryIt != Maps.end());
size_t HotIndex = std::distance(Maps.begin(), HotEntryIt);
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
} else {
Expand All @@ -207,7 +208,7 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
LLVM_DEBUG(dbgs() << "Hash: " << formatv("{0:x}\n", BFHash));
OS.write(reinterpret_cast<char *>(&BFHash), 8);
// Number of basic blocks
size_t NumBasicBlocks = getBBHashMap(HotInputAddress).getNumBasicBlocks();
size_t NumBasicBlocks = NumBasicBlocksMap[HotInputAddress];
LLVM_DEBUG(dbgs() << "Basic blocks: " << NumBasicBlocks << '\n');
encodeULEB128(NumBasicBlocks, OS);
// Secondary entry points
Expand Down Expand Up @@ -425,8 +426,9 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
for (const auto &MapEntry : Maps) {
const uint64_t Address = MapEntry.first;
const uint64_t HotAddress = fetchParentAddress(Address);
const bool IsHotFunction = HotAddress == 0;
OS << "Function Address: 0x" << Twine::utohexstr(Address);
if (HotAddress == 0)
if (IsHotFunction)
OS << formatv(", hash: {0:x}", getBFHash(Address));
OS << "\n";
OS << "BB mappings:\n";
Expand All @@ -443,6 +445,8 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
OS << formatv(" hash: {0:x}", BBHashMap.getBBHash(Val));
OS << "\n";
}
if (IsHotFunction)
OS << "NumBlocks: " << NumBasicBlocksMap[Address] << '\n';
if (SecondaryEntryPointsMap.count(Address)) {
const std::vector<uint32_t> &SecondaryEntryPoints =
SecondaryEntryPointsMap[Address];
Expand Down Expand Up @@ -574,6 +578,7 @@ void BoltAddressTranslation::saveMetadata(BinaryContext &BC) {
// Set BF/BB metadata
for (const BinaryBasicBlock &BB : BF)
BBHashMap.addEntry(BB.getInputOffset(), BB.getIndex(), BB.getHash());
NumBasicBlocksMap.emplace(BF.getAddress(), BF.size());
}
}

Expand All @@ -597,5 +602,49 @@ BoltAddressTranslation::getBFBranches(uint64_t OutputAddress) const {
return Branches;
}

unsigned
BoltAddressTranslation::getSecondaryEntryPointId(uint64_t Address,
uint32_t Offset) const {
auto FunctionIt = SecondaryEntryPointsMap.find(Address);
if (FunctionIt == SecondaryEntryPointsMap.end())
return 0;
const std::vector<uint32_t> &Offsets = FunctionIt->second;
auto OffsetIt = std::find(Offsets.begin(), Offsets.end(), Offset);
if (OffsetIt == Offsets.end())
return 0;
// Adding one here because main entry point is not stored in BAT, and
// enumeration for secondary entry points starts with 1.
return OffsetIt - Offsets.begin() + 1;
}

std::pair<const BinaryFunction *, unsigned>
BoltAddressTranslation::translateSymbol(const BinaryContext &BC,
const MCSymbol &Symbol,
uint32_t Offset) const {
// The symbol could be a secondary entry in a cold fragment.
uint64_t SymbolValue = cantFail(errorOrToExpected(BC.getSymbolValue(Symbol)));

const BinaryFunction *Callee = BC.getFunctionForSymbol(&Symbol);
assert(Callee);

// Containing function, not necessarily the same as symbol value.
const uint64_t CalleeAddress = Callee->getAddress();
const uint32_t OutputOffset = SymbolValue - CalleeAddress;

const uint64_t ParentAddress = fetchParentAddress(CalleeAddress);
const uint64_t HotAddress = ParentAddress ? ParentAddress : CalleeAddress;

const BinaryFunction *ParentBF = BC.getBinaryFunctionAtAddress(HotAddress);

const uint32_t InputOffset =
translate(CalleeAddress, OutputOffset, /*IsBranchSrc*/ false) + Offset;

unsigned SecondaryEntryId{0};
if (InputOffset)
SecondaryEntryId = getSecondaryEntryPointId(HotAddress, InputOffset);

return std::pair(ParentBF, SecondaryEntryId);
}

} // namespace bolt
} // namespace llvm
2 changes: 1 addition & 1 deletion bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2333,7 +2333,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
if (BAT->isBATFunction(Function.getAddress()))
continue;
BP.Functions.emplace_back(
YAMLProfileWriter::convert(Function, /*UseDFS=*/false));
YAMLProfileWriter::convert(Function, /*UseDFS=*/false, BAT));
}

for (const auto &KV : NamesToBranches) {
Expand Down
23 changes: 14 additions & 9 deletions bolt/lib/Profile/YAMLProfileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "bolt/Profile/YAMLProfileWriter.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Profile/ProfileReaderBase.h"
#include "bolt/Rewrite/RewriteInstance.h"
#include "llvm/Support/CommandLine.h"
Expand All @@ -25,17 +26,19 @@ extern llvm::cl::opt<bool> ProfileUseDFS;
namespace llvm {
namespace bolt {

/// Set CallSiteInfo destination fields from \p Symbol and return a target
/// BinaryFunction for that symbol.
static const BinaryFunction *setCSIDestination(const BinaryContext &BC,
yaml::bolt::CallSiteInfo &CSI,
const MCSymbol *Symbol) {
const BinaryFunction *YAMLProfileWriter::setCSIDestination(
const BinaryContext &BC, yaml::bolt::CallSiteInfo &CSI,
const MCSymbol *Symbol, const BoltAddressTranslation *BAT,
uint32_t Offset) {
CSI.DestId = 0; // designated for unknown functions
CSI.EntryDiscriminator = 0;

if (Symbol) {
uint64_t EntryID = 0;
if (const BinaryFunction *const Callee =
if (const BinaryFunction *Callee =
BC.getFunctionForSymbol(Symbol, &EntryID)) {
if (BAT && BAT->isBATFunction(Callee->getAddress()))
std::tie(Callee, EntryID) = BAT->translateSymbol(BC, *Symbol, Offset);
CSI.DestId = Callee->getFunctionNumber();
CSI.EntryDiscriminator = EntryID;
return Callee;
Expand All @@ -45,7 +48,8 @@ static const BinaryFunction *setCSIDestination(const BinaryContext &BC,
}

yaml::bolt::BinaryFunctionProfile
YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS,
const BoltAddressTranslation *BAT) {
yaml::bolt::BinaryFunctionProfile YamlBF;
const BinaryContext &BC = BF.getBinaryContext();

Expand Down Expand Up @@ -98,7 +102,8 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
continue;
for (const IndirectCallProfile &CSP : ICSP.get()) {
StringRef TargetName = "";
const BinaryFunction *Callee = setCSIDestination(BC, CSI, CSP.Symbol);
const BinaryFunction *Callee =
setCSIDestination(BC, CSI, CSP.Symbol, BAT);
if (Callee)
TargetName = Callee->getOneName();
CSI.Count = CSP.Count;
Expand All @@ -109,7 +114,7 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
StringRef TargetName = "";
const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr);
const BinaryFunction *const Callee =
setCSIDestination(BC, CSI, CalleeSymbol);
setCSIDestination(BC, CSI, CalleeSymbol, BAT);
if (Callee)
TargetName = Callee->getOneName();

Expand Down
7 changes: 6 additions & 1 deletion bolt/test/X86/bolt-address-translation-yaml.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ YAML-BAT-CHECK-NEXT: - bid: 0
YAML-BAT-CHECK-NEXT: insns: 26
YAML-BAT-CHECK-NEXT: hash: 0xA900AE79CFD40000
YAML-BAT-CHECK-NEXT: succ: [ { bid: 3, cnt: 0 }, { bid: 1, cnt: 0 } ]
# Calls from no-BAT to BAT function
YAML-BAT-CHECK: - bid: 28
YAML-BAT-CHECK-NEXT: insns: 13
YAML-BAT-CHECK-NEXT: hash: 0xB2F04C1F25F00400
YAML-BAT-CHECK-NEXT: calls: [ { off: 0x21, fid: [[#SOLVECUBIC:]], cnt: 25 }, { off: 0x2D, fid: [[#]], cnt: 9 } ]
# Function covered by BAT with calls
YAML-BAT-CHECK: - name: SolveCubic
YAML-BAT-CHECK-NEXT: fid: [[#]]
YAML-BAT-CHECK-NEXT: fid: [[#SOLVECUBIC]]
YAML-BAT-CHECK-NEXT: hash: 0x6AF7E61EA3966722
YAML-BAT-CHECK-NEXT: exec: 25
YAML-BAT-CHECK-NEXT: nblocks: 15
Expand Down
19 changes: 16 additions & 3 deletions bolt/test/X86/yaml-secondary-entry-discriminator.s
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
# RUN: FileCheck %s -input-file %t.yaml
# CHECK: - name: main
# CHECK-NEXT: fid: 2
# CHECK-NEXT: hash: 0xADF270D550151185
# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: exec: 0
# CHECK-NEXT: nblocks: 4
# CHECK-NEXT: blocks:
# CHECK: - bid: 1
# CHECK-NEXT: insns: 1
# CHECK-NEXT: hash: 0x36A303CBA4360014
# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: calls: [ { off: 0x0, fid: 1, disc: 1, cnt: 1 } ]
# CHECK: - bid: 2
# CHECK-NEXT: insns: 5
# CHECK-NEXT: hash: 0x8B2F5747CD0019
# CHECK-NEXT: hash: {{.*}}
# CHECK-NEXT: calls: [ { off: 0x0, fid: 1, disc: 1, cnt: 1, mis: 1 } ]

# Make sure that the profile is attached correctly
Expand All @@ -33,15 +33,28 @@
# CHECK-CFG: callq *%rax # Offset: [[#]] # CallProfile: 1 (1 misses) :
# CHECK-CFG-NEXT: { secondary_entry: 1 (1 misses) }

# YAML BAT test of calling BAT secondary entry from non-BAT function
# Now force-split func and skip main (making it call secondary entries)
# RUN: llvm-bolt %t.exe -o %t.bat --data %t.fdata --funcs=func \
# RUN: --split-functions --split-strategy=all --split-all-cold --enable-bat

.globl func
.type func, @function
func:
# FDATA: 0 [unknown] 0 1 func 0 1 0
.cfi_startproc
pushq %rbp
movq %rsp, %rbp
# Placeholder code to make splitting profitable
.rept 5
testq %rax, %rax
.endr
.globl secondary_entry
secondary_entry:
# Placeholder code to make splitting profitable
.rept 5
testq %rax, %rax
.endr
popq %rbp
retq
nopl (%rax)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@
//===----------------------------------------------------------------------===//

#include "AvoidReturnWithVoidValueCheck.h"
#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "../utils/BracesAroundStatement.h"
#include "../utils/LexerUtils.h"

using namespace clang::ast_matchers;

namespace clang::tidy::readability {

static constexpr auto IgnoreMacrosName = "IgnoreMacros";
static constexpr auto IgnoreMacrosDefault = true;
static constexpr char IgnoreMacrosName[] = "IgnoreMacros";
static const bool IgnoreMacrosDefault = true;

static constexpr auto StrictModeName = "StrictMode";
static constexpr auto StrictModeDefault = true;
static constexpr char StrictModeName[] = "StrictMode";
static const bool StrictModeDefault = true;

AvoidReturnWithVoidValueCheck::AvoidReturnWithVoidValueCheck(
StringRef Name, ClangTidyContext *Context)
Expand All @@ -32,7 +31,10 @@ void AvoidReturnWithVoidValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(
hasReturnValue(allOf(hasType(voidType()), unless(initListExpr()))),
optionally(hasParent(compoundStmt().bind("compound_parent"))))
optionally(hasParent(
compoundStmt(
optionally(hasParent(functionDecl().bind("function_parent"))))
.bind("compound_parent"))))
.bind("void_return"),
this);
}
Expand All @@ -42,10 +44,30 @@ void AvoidReturnWithVoidValueCheck::check(
const auto *VoidReturn = Result.Nodes.getNodeAs<ReturnStmt>("void_return");
if (IgnoreMacros && VoidReturn->getBeginLoc().isMacroID())
return;
if (!StrictMode && !Result.Nodes.getNodeAs<CompoundStmt>("compound_parent"))
const auto *SurroundingBlock =
Result.Nodes.getNodeAs<CompoundStmt>("compound_parent");
if (!StrictMode && !SurroundingBlock)
return;
diag(VoidReturn->getBeginLoc(), "return statement within a void function "
"should not have a specified return value");
DiagnosticBuilder Diag = diag(VoidReturn->getBeginLoc(),
"return statement within a void function "
"should not have a specified return value");
const SourceLocation SemicolonPos = utils::lexer::findNextTerminator(
VoidReturn->getEndLoc(), *Result.SourceManager, getLangOpts());
if (SemicolonPos.isInvalid())
return;
if (!SurroundingBlock) {
const auto BraceInsertionHints = utils::getBraceInsertionsHints(
VoidReturn, getLangOpts(), *Result.SourceManager,
VoidReturn->getBeginLoc());
if (BraceInsertionHints)
Diag << BraceInsertionHints.openingBraceFixIt()
<< BraceInsertionHints.closingBraceFixIt();
}
Diag << FixItHint::CreateRemoval(VoidReturn->getReturnLoc());
if (!Result.Nodes.getNodeAs<FunctionDecl>("function_parent") ||
SurroundingBlock->body_back() != VoidReturn)
Diag << FixItHint::CreateInsertion(SemicolonPos.getLocWithOffset(1),
" return;", true);
}

void AvoidReturnWithVoidValueCheck::storeOptions(
Expand Down
157 changes: 25 additions & 132 deletions clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "BracesAroundStatementsCheck.h"
#include "../utils/BracesAroundStatement.h"
#include "../utils/LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
Expand All @@ -17,12 +18,10 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {

static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
Token Tok;
SourceLocation Beginning =
Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
const bool Invalid =
Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
assert(!Invalid && "Expected a valid token.");

if (Invalid)
Expand All @@ -33,64 +32,21 @@ static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,

static SourceLocation
forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
assert(Loc.isValid());
for (;;) {
while (isWhitespace(*SM.getCharacterData(Loc)))
Loc = Loc.getLocWithOffset(1);

tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
if (TokKind != tok::comment)
return Loc;

// Fast-forward current token.
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
}
}

static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
const ASTContext *Context) {
SourceLocation Loc =
utils::lexer::getUnifiedEndLoc(S, SM, Context->getLangOpts());
if (!Loc.isValid())
return Loc;

// Start searching right after S.
Loc = Loc.getLocWithOffset(1);

for (;;) {
assert(Loc.isValid());
while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
Loc = Loc.getLocWithOffset(1);
}

if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
// EOL, insert brace before.
break;
}
tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
if (TokKind != tok::comment) {
// Non-comment token, insert brace before.
break;
}

SourceLocation TokEndLoc =
Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
SourceRange TokRange(Loc, TokEndLoc);
StringRef Comment = Lexer::getSourceText(
CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
if (Comment.starts_with("/*") && Comment.contains('\n')) {
// Multi-line block comment, insert brace before.
break;
}
// else: Trailing comment, insert brace after the newline.

// Fast-forward current token.
Loc = TokEndLoc;
}
return Loc;
}

BracesAroundStatementsCheck::BracesAroundStatementsCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Expand Down Expand Up @@ -124,7 +80,7 @@ void BracesAroundStatementsCheck::check(
} else if (const auto *S = Result.Nodes.getNodeAs<DoStmt>("do")) {
checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
} else if (const auto *S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
SourceLocation StartLoc = findRParenLoc(S, SM, Context);
SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
checkStmt(Result, S->getBody(), StartLoc);
Expand All @@ -133,7 +89,7 @@ void BracesAroundStatementsCheck::check(
if (S->isConsteval())
return;

SourceLocation StartLoc = findRParenLoc(S, SM, Context);
SourceLocation StartLoc = findRParenLoc(S, SM, Context->getLangOpts());
if (StartLoc.isInvalid())
return;
if (ForceBracesStmts.erase(S))
Expand All @@ -156,7 +112,7 @@ template <typename IfOrWhileStmt>
SourceLocation
BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
const SourceManager &SM,
const ASTContext *Context) {
const LangOptions &LangOpts) {
// Skip macros.
if (S->getBeginLoc().isMacroID())
return {};
Expand All @@ -170,14 +126,14 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
}

SourceLocation PastCondEndLoc =
Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);
if (PastCondEndLoc.isInvalid())
return {};
SourceLocation RParenLoc =
forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, LangOpts);
if (RParenLoc.isInvalid())
return {};
tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, LangOpts);
if (TokKind != tok::r_paren)
return {};
return RParenLoc;
Expand All @@ -188,86 +144,23 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
bool BracesAroundStatementsCheck::checkStmt(
const MatchFinder::MatchResult &Result, const Stmt *S,
SourceLocation StartLoc, SourceLocation EndLocHint) {

while (const auto *AS = dyn_cast<AttributedStmt>(S))
S = AS->getSubStmt();

const SourceManager &SM = *Result.SourceManager;
const ASTContext *Context = Result.Context;

// 1) If there's a corresponding "else" or "while", the check inserts "} "
// right before that token.
// 2) If there's a multi-line block comment starting on the same line after
// the location we're inserting the closing brace at, or there's a non-comment
// token, the check inserts "\n}" right before that token.
// 3) Otherwise the check finds the end of line (possibly after some block or
// line comments) and inserts "\n}" right before that EOL.
if (!S || isa<CompoundStmt>(S)) {
// Already inside braces.
return false;
}

// When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
// This NullStmt can be detected according to beginning token.
const SourceLocation StmtBeginLoc = S->getBeginLoc();
if (isa<NullStmt>(S) && StmtBeginLoc.isValid() &&
getTokenKind(StmtBeginLoc, SM, Context) == tok::l_brace)
return false;

if (StartLoc.isInvalid())
return false;

// Convert StartLoc to file location, if it's on the same macro expansion
// level as the start of the statement. We also need file locations for
// Lexer::getLocForEndOfToken working properly.
StartLoc = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
Context->getLangOpts())
.getBegin();
if (StartLoc.isInvalid())
return false;
StartLoc =
Lexer::getLocForEndOfToken(StartLoc, 0, SM, Context->getLangOpts());

// StartLoc points at the location of the opening brace to be inserted.
SourceLocation EndLoc;
std::string ClosingInsertion;
if (EndLocHint.isValid()) {
EndLoc = EndLocHint;
ClosingInsertion = "} ";
} else {
EndLoc = findEndLocation(*S, SM, Context);
ClosingInsertion = "\n}";
}

assert(StartLoc.isValid());

// Don't require braces for statements spanning less than certain number of
// lines.
if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
if (EndLine - StartLine < ShortStatementLines)
const auto BraceInsertionHints = utils::getBraceInsertionsHints(
S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,
EndLocHint);
if (BraceInsertionHints) {
if (ShortStatementLines && !ForceBracesStmts.erase(S) &&
BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <
ShortStatementLines)
return false;
auto Diag = diag(BraceInsertionHints.DiagnosticPos,
"statement should be inside braces");
if (BraceInsertionHints.offersFixIts())
Diag << BraceInsertionHints.openingBraceFixIt()
<< BraceInsertionHints.closingBraceFixIt();
}

auto Diag = diag(StartLoc, "statement should be inside braces");

// Change only if StartLoc and EndLoc are on the same macro expansion level.
// This will also catch invalid EndLoc.
// Example: LLVM_DEBUG( for(...) do_something() );
// In this case fix-it cannot be provided as the semicolon which is not
// visible here is part of the macro. Adding braces here would require adding
// another semicolon.
if (Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(SourceRange(
SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
SM, Context->getLangOpts())
.isInvalid())
return false;

Diag << FixItHint::CreateInsertion(StartLoc, " {")
<< FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class BracesAroundStatementsCheck : public ClangTidyCheck {
SourceLocation EndLocHint = SourceLocation());
template <typename IfOrWhileStmt>
SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
const ASTContext *Context);
const LangOptions &LangOpts);
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ void DuplicateIncludeCallbacks::InclusionDirective(
bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
// Skip includes behind macros
if (FilenameRange.getBegin().isMacroID() ||
FilenameRange.getEnd().isMacroID())
return;
if (llvm::is_contained(Files.back(), FileName)) {
// We want to delete the entire line, so make sure that [Start,End] covers
// everything.
Expand Down
168 changes: 168 additions & 0 deletions clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//===--- BracesAroundStatement.cpp - clang-tidy -------- ------------------===//
//
// 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
/// This file provides utilities to put braces around a statement.
///
//===----------------------------------------------------------------------===//

#include "BracesAroundStatement.h"
#include "../utils/LexerUtils.h"
#include "LexerUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/Lexer.h"

namespace clang::tidy::utils {

BraceInsertionHints::operator bool() const { return DiagnosticPos.isValid(); }

bool BraceInsertionHints::offersFixIts() const {
return OpeningBracePos.isValid() && ClosingBracePos.isValid();
}

unsigned BraceInsertionHints::resultingCompoundLineExtent(
const SourceManager &SourceMgr) const {
return SourceMgr.getSpellingLineNumber(ClosingBracePos) -
SourceMgr.getSpellingLineNumber(OpeningBracePos);
}

FixItHint BraceInsertionHints::openingBraceFixIt() const {
return OpeningBracePos.isValid()
? FixItHint::CreateInsertion(OpeningBracePos, " {")
: FixItHint();
}

FixItHint BraceInsertionHints::closingBraceFixIt() const {
return ClosingBracePos.isValid()
? FixItHint::CreateInsertion(ClosingBracePos, ClosingBrace)
: FixItHint();
}

static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
const LangOptions &LangOpts) {
Token Tok;
SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);
assert(!Invalid && "Expected a valid token.");

if (Invalid)
return tok::NUM_TOKENS;

return Tok.getKind();
}

static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation Loc = lexer::getUnifiedEndLoc(S, SM, LangOpts);
if (!Loc.isValid())
return Loc;

// Start searching right after S.
Loc = Loc.getLocWithOffset(1);

for (;;) {
assert(Loc.isValid());
while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
Loc = Loc.getLocWithOffset(1);
}

if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
// EOL, insert brace before.
break;
}
tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);
if (TokKind != tok::comment) {
// Non-comment token, insert brace before.
break;
}

SourceLocation TokEndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
SourceRange TokRange(Loc, TokEndLoc);
StringRef Comment = Lexer::getSourceText(
CharSourceRange::getTokenRange(TokRange), SM, LangOpts);
if (Comment.starts_with("/*") && Comment.contains('\n')) {
// Multi-line block comment, insert brace before.
break;
}
// else: Trailing comment, insert brace after the newline.

// Fast-forward current token.
Loc = TokEndLoc;
}
return Loc;
}

BraceInsertionHints getBraceInsertionsHints(const Stmt *const S,
const LangOptions &LangOpts,
const SourceManager &SM,
SourceLocation StartLoc,
SourceLocation EndLocHint) {
// 1) If there's a corresponding "else" or "while", the check inserts "} "
// right before that token.
// 2) If there's a multi-line block comment starting on the same line after
// the location we're inserting the closing brace at, or there's a non-comment
// token, the check inserts "\n}" right before that token.
// 3) Otherwise the check finds the end of line (possibly after some block or
// line comments) and inserts "\n}" right before that EOL.
if (!S || isa<CompoundStmt>(S)) {
// Already inside braces.
return {};
}

// When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.
// This NullStmt can be detected according to beginning token.
const SourceLocation StmtBeginLoc = S->getBeginLoc();
if (isa<NullStmt>(S) && StmtBeginLoc.isValid() &&
getTokenKind(StmtBeginLoc, SM, LangOpts) == tok::l_brace)
return {};

if (StartLoc.isInvalid())
return {};

// Convert StartLoc to file location, if it's on the same macro expansion
// level as the start of the statement. We also need file locations for
// Lexer::getLocForEndOfToken working properly.
StartLoc = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,
LangOpts)
.getBegin();
if (StartLoc.isInvalid())
return {};
StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOpts);

// StartLoc points at the location of the opening brace to be inserted.
SourceLocation EndLoc;
std::string ClosingInsertion;
if (EndLocHint.isValid()) {
EndLoc = EndLocHint;
ClosingInsertion = "} ";
} else {
EndLoc = findEndLocation(*S, SM, LangOpts);
ClosingInsertion = "\n}";
}

assert(StartLoc.isValid());

// Change only if StartLoc and EndLoc are on the same macro expansion level.
// This will also catch invalid EndLoc.
// Example: LLVM_DEBUG( for(...) do_something() );
// In this case fix-it cannot be provided as the semicolon which is not
// visible here is part of the macro. Adding braces here would require adding
// another semicolon.
if (Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(SourceRange(
SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),
SM, LangOpts)
.isInvalid())
return {StartLoc};
return {StartLoc, EndLoc, ClosingInsertion};
}

} // namespace clang::tidy::utils
75 changes: 75 additions & 0 deletions clang-tools-extra/clang-tidy/utils/BracesAroundStatement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===--- BracesAroundStatement.h - clang-tidy ------- -----------*- 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
/// This file provides utilities to put braces around a statement.
///
//===----------------------------------------------------------------------===//

#include "clang/AST/Stmt.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"

namespace clang::tidy::utils {

/// A provider of fix-it hints to insert opening and closing braces. An instance
/// of this type is the result of calling `getBraceInsertionsHints` below.
struct BraceInsertionHints {
/// The position of a potential diagnostic. It coincides with the position of
/// the opening brace to insert, but can also just be the place to show a
/// diagnostic in case braces cannot be inserted automatically.
SourceLocation DiagnosticPos;

/// Constructor for a no-hint.
BraceInsertionHints() = default;

/// Constructor for a valid hint that cannot insert braces automatically.
BraceInsertionHints(SourceLocation DiagnosticPos)
: DiagnosticPos(DiagnosticPos) {}

/// Constructor for a hint offering fix-its for brace insertion. Both
/// positions must be valid.
BraceInsertionHints(SourceLocation OpeningBracePos,
SourceLocation ClosingBracePos, std::string ClosingBrace)
: DiagnosticPos(OpeningBracePos), OpeningBracePos(OpeningBracePos),
ClosingBracePos(ClosingBracePos), ClosingBrace(ClosingBrace) {
assert(offersFixIts());
}

/// Indicates whether the hint provides at least the position of a diagnostic.
operator bool() const;

/// Indicates whether the hint provides fix-its to insert braces.
bool offersFixIts() const;

/// The number of lines between the inserted opening brace and its closing
/// counterpart.
unsigned resultingCompoundLineExtent(const SourceManager &SourceMgr) const;

/// Fix-it to insert an opening brace.
FixItHint openingBraceFixIt() const;

/// Fix-it to insert a closing brace.
FixItHint closingBraceFixIt() const;

private:
SourceLocation OpeningBracePos;
SourceLocation ClosingBracePos;
std::string ClosingBrace;
};

/// Create fix-it hints for braces that wrap the given statement when applied.
/// The algorithm computing them respects comment before and after the statement
/// and adds line breaks before the braces accordingly.
BraceInsertionHints
getBraceInsertionsHints(const Stmt *const S, const LangOptions &LangOpts,
const SourceManager &SM, SourceLocation StartLoc,
SourceLocation EndLocHint = SourceLocation());

} // namespace clang::tidy::utils
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyUtils
Aliasing.cpp
ASTUtils.cpp
BracesAroundStatement.cpp
DeclRefExprUtils.cpp
DesignatedInitializers.cpp
ExceptionAnalyzer.cpp
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,14 @@ Changes in existing checks
analyzed, se the check now handles the common patterns
`const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`.

- Improved :doc:`readability-avoid-return-with-void-value
<clang-tidy/checks/readability/avoid-return-with-void-value>` check by adding
fix-its.

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.

- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check in `GetConfigPerFile`
mode by resolving symbolic links to header files. Fixed handling of Hungarian
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,30 @@ void f2() {
return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f1();
}

void f3(bool b) {
if (b) return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: if (b) { f1(); return;
// CHECK-NEXT: }
return f2();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f2();
// CHECK-FIXES-LENIENT: f2();
}

template<class T>
T f4() {}

void f5() {
return f4<void>();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
{ return f4<void>(); }
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:7: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: { f4<void>(); return; }
// CHECK-FIXES-LENIENT: { f4<void>(); return; }
}

void f6() { return; }
Expand All @@ -41,6 +48,8 @@ void f9() {
return (void)f7();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: (void)f7();
// CHECK-FIXES-LENIENT: (void)f7();
}

#define RETURN_VOID return (void)1
Expand All @@ -50,12 +59,12 @@ void f10() {
// CHECK-MESSAGES-INCLUDE-MACROS: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
}

template <typename A>
template <typename A>
struct C {
C(A) {}
};

template <class T>
template <class T>
C<T> f11() { return {}; }

using VOID = void;
Expand All @@ -66,4 +75,36 @@ VOID f13() {
return f12();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: f12(); return;
// CHECK-FIXES-LENIENT: f12(); return;
(void)1;
}

void f14() {
return /* comment */ f1() /* comment */ ;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: /* comment */ f1() /* comment */ ; return;
// CHECK-FIXES-LENIENT: /* comment */ f1() /* comment */ ; return;
(void)1;
}

void f15() {
return/*comment*/f1()/*comment*/;//comment
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-MESSAGES-LENIENT: :[[@LINE-2]]:5: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: /*comment*/f1()/*comment*/; return;//comment
// CHECK-FIXES-LENIENT: /*comment*/f1()/*comment*/; return;//comment
(void)1;
}

void f16(bool b) {
if (b) return f1();
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: if (b) { f1(); return;
// CHECK-NEXT: }
else return f2();
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: return statement within a void function should not have a specified return value [readability-avoid-return-with-void-value]
// CHECK-FIXES: else { f2(); return;
// CHECK-NEXT: }
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ int r;
// CHECK-FIXES: {{^int q;$}}
// CHECK-FIXES-NEXT: {{^#include <sys/types.h>$}}
// CHECK-FIXES-NEXT: {{^int r;$}}

namespace Issue_87303 {
#define RESET_INCLUDE_CACHE
// Expect no warnings

#define MACRO_FILENAME "duplicate-include.h"
#include MACRO_FILENAME
#include "duplicate-include.h"

#define MACRO_FILENAME_2 <duplicate-include2.h>
#include <duplicate-include2.h>
#include MACRO_FILENAME_2

#undef RESET_INCLUDE_CACHE
} // Issue_87303
1 change: 1 addition & 0 deletions clang/cmake/caches/Apple-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
set(LLVM_ENABLE_MODULES ON CACHE BOOL "")
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
set(LLVM_ENABLE_EXPORTED_SYMBOLS_IN_EXECUTABLES OFF CACHE BOOL "")
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
set(CLANG_SPAWN_CC1 ON CACHE BOOL "")
set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "")
Expand Down
21 changes: 17 additions & 4 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3138,10 +3138,16 @@ are detected:
allowed in this state.
* Invalid 3rd ("``whence``") argument to ``fseek``.
The checker does not track the correspondence between integer file descriptors
and ``FILE *`` pointers. Operations on standard streams like ``stdin`` are not
treated specially and are therefore often not recognized (because these streams
are usually not opened explicitly by the program, and are global variables).
The stream operations are by this checker usually split into two cases, a success
and a failure case. However, in the case of write operations (like ``fwrite``,
``fprintf`` and even ``fsetpos``) this behavior could produce a large amount of
unwanted reports on projects that don't have error checks around the write
operations, so by default the checker assumes that write operations always succeed.
This behavior can be controlled by the ``Pedantic`` flag: With
``-analyzer-config alpha.unix.Stream:Pedantic=true`` the checker will model the
cases where a write operation fails and report situations where this leads to
erroneous behavior. (The default is ``Pedantic=false``, where write operations
are assumed to succeed.)
.. code-block:: c
Expand Down Expand Up @@ -3196,6 +3202,13 @@ are usually not opened explicitly by the program, and are global variables).
fclose(p);
}
**Limitations**
The checker does not track the correspondence between integer file descriptors
and ``FILE *`` pointers. Operations on standard streams like ``stdin`` are not
treated specially and are therefore often not recognized (because these streams
are usually not opened explicitly by the program, and are global variables).
.. _alpha-unix-cstring-BufferOverlap:
alpha.unix.cstring.BufferOverlap (C)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ def warn_drv_darwin_sdk_invalid_settings : Warning<
"SDK settings were ignored as 'SDKSettings.json' could not be parsed">,
InGroup<DiagGroup<"darwin-sdk-settings">>;

def err_missing_sysroot : Error<"no such sysroot directory: '%0'">;
def err_drv_darwin_sdk_missing_arclite : Error<
"SDK does not contain 'libarclite' at the path '%0'; try increasing the minimum deployment target">;

Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ def err_no_such_header_file : Error<"no such %select{public|private|project}1 he
def warn_no_such_excluded_header_file : Warning<"no such excluded %select{public|private}0 header file: '%1'">, InGroup<InstallAPIViolation>;
def warn_glob_did_not_match: Warning<"glob '%0' did not match any header file">, InGroup<InstallAPIViolation>;
def err_no_such_umbrella_header_file : Error<"%select{public|private|project}1 umbrella header file not found in input: '%0'">;
def err_cannot_find_reexport : Error<"cannot find re-exported %select{framework|library}0: '%1'">;
} // end of command line category.

let CategoryName = "Verification" in {
// Diagnostics about symbols.
def warn_target: Warning<"violations found for %0">, InGroup<InstallAPIViolation>;
def err_library_missing_symbol : Error<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">;
def warn_library_missing_symbol : Warning<"declaration has external linkage, but dynamic library doesn't have symbol '%0'">, InGroup<InstallAPIViolation>;
Expand All @@ -43,6 +45,25 @@ def err_dylib_symbol_flags_mismatch : Error<"dynamic library symbol '%0' is "
"%select{weak defined|thread local}1, but its declaration is not">;
def err_header_symbol_flags_mismatch : Error<"declaration '%0' is "
"%select{weak defined|thread local}1, but symbol is not in dynamic library">;

// Diagnostics about load commands.
def err_architecture_mismatch : Error<"architectures do not match: '%0' (provided) vs '%1' (found)">;
def warn_platform_mismatch : Warning<"platform does not match: '%0' (provided) vs '%1' (found)">, InGroup<InstallAPIViolation>;
def err_platform_mismatch : Error<"platform does not match: '%0' (provided) vs '%1' (found)">;
def err_install_name_mismatch : Error<"install_name does not match: '%0' (provided) vs '%1' (found)">;
def err_current_version_mismatch : Error<"current_version does not match: '%0' (provided) vs '%1' (found)">;
def err_compatibility_version_mismatch : Error<"compatibility_version does not match: '%0' (provided) vs '%1' (found)">;
def err_appextension_safe_mismatch : Error<"ApplicationExtensionSafe flag does not match: '%0' (provided) vs '%1' (found)">;
def err_shared_cache_eligiblity_mismatch : Error<"NotForDyldSharedCache flag does not match: '%0' (provided) vs '%1' (found)">;
def err_no_twolevel_namespace : Error<"flat namespace libraries are not supported">;
def err_parent_umbrella_missing: Error<"parent umbrella missing from %0: '%1'">;
def err_parent_umbrella_mismatch : Error<"parent umbrella does not match: '%0' (provided) vs '%1' (found)">;
def err_reexported_libraries_missing : Error<"re-exported library missing from %0: '%1'">;
def err_reexported_libraries_mismatch : Error<"re-exported libraries do not match: '%0' (provided) vs '%1' (found)">;
def err_allowable_clients_missing : Error<"allowable client missing from %0: '%1'">;
def err_allowable_clients_mismatch : Error<"allowable clients do not match: '%0' (provided) vs '%1' (found)">;
def warn_rpaths_missing : Warning<"runpath search paths missing from %0: '%1'">, InGroup<InstallAPIViolation>;
def warn_rpaths_mismatch : Warning<"runpath search paths do not match: '%0' (provided) vs '%1' (found)">, InGroup<InstallAPIViolation>;
} // end of Verification category.

} // end of InstallAPI component
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def ext_vla_folded_to_constant : ExtWarn<
"variable length array folded to constant array as an extension">,
InGroup<GNUFoldingConstant>;
def err_vla_unsupported : Error<
"variable length arrays are not supported for %select{the current target|'%1'}0">;
"variable length arrays are not supported %select{for the current target|in '%1'}0">;
def err_vla_in_coroutine_unsupported : Error<
"variable length arrays in a coroutine are not supported">;
def note_vla_unsupported : Note<
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,8 @@ def : Joined<["-"], "Xclang=">, Group<CompileOnly_Group>,
def Xcuda_fatbinary : Separate<["-"], "Xcuda-fatbinary">,
HelpText<"Pass <arg> to fatbinary invocation">, MetaVarName<"<arg>">;
def Xcuda_ptxas : Separate<["-"], "Xcuda-ptxas">,
HelpText<"Pass <arg> to the ptxas assembler">, MetaVarName<"<arg>">;
HelpText<"Pass <arg> to the ptxas assembler">, MetaVarName<"<arg>">,
Visibility<[ClangOption, CLOption]>;
def Xopenmp_target : Separate<["-"], "Xopenmp-target">, Group<CompileOnly_Group>,
HelpText<"Pass <arg> to the target offloading toolchain.">, MetaVarName<"<arg>">;
def Xopenmp_target_EQ : JoinedAndSeparate<["-"], "Xopenmp-target=">, Group<CompileOnly_Group>,
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/InstallAPI/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct InstallAPIContext {
/// Library attributes that are typically passed as linker inputs.
BinaryAttrs BA;

/// Install names of reexported libraries of a library.
LibAttrs Reexports;

/// All headers that represent a library.
HeaderSeq InputHeaders;

Expand Down Expand Up @@ -80,6 +83,20 @@ struct InstallAPIContext {
llvm::DenseMap<StringRef, HeaderType> KnownIncludes;
};

/// Lookup the dylib or TextAPI file location for a system library or framework.
/// The search paths provided are searched in order.
/// @rpath based libraries are not supported.
///
/// \param InstallName The install name for the library.
/// \param FrameworkSearchPaths Search paths to look up frameworks with.
/// \param LibrarySearchPaths Search paths to look up dylibs with.
/// \param SearchPaths Fallback search paths if library was not found in earlier
/// paths.
/// \return The full path of the library.
std::string findLibrary(StringRef InstallName, FileManager &FM,
ArrayRef<std::string> FrameworkSearchPaths,
ArrayRef<std::string> LibrarySearchPaths,
ArrayRef<std::string> SearchPaths);
} // namespace installapi
} // namespace clang

Expand Down
29 changes: 24 additions & 5 deletions clang/include/clang/InstallAPI/DylibVerifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ enum class VerificationMode {
Pedantic,
};

using LibAttrs = llvm::StringMap<ArchitectureSet>;
using ReexportedInterfaces = llvm::SmallVector<llvm::MachO::InterfaceFile, 8>;

/// Service responsible to tracking state of verification across the
/// lifetime of InstallAPI.
/// As declarations are collected during AST traversal, they are
Expand Down Expand Up @@ -63,11 +66,12 @@ class DylibVerifier : llvm::MachO::RecordVisitor {

DylibVerifier() = default;

DylibVerifier(llvm::MachO::Records &&Dylib, DiagnosticsEngine *Diag,
VerificationMode Mode, bool Demangle, StringRef DSYMPath)
: Dylib(std::move(Dylib)), Mode(Mode), Demangle(Demangle),
DSYMPath(DSYMPath), Exports(std::make_unique<SymbolSet>()),
Ctx(VerifierContext{Diag}) {}
DylibVerifier(llvm::MachO::Records &&Dylib, ReexportedInterfaces &&Reexports,
DiagnosticsEngine *Diag, VerificationMode Mode, bool Demangle,
StringRef DSYMPath)
: Dylib(std::move(Dylib)), Reexports(std::move(Reexports)), Mode(Mode),
Demangle(Demangle), DSYMPath(DSYMPath),
Exports(std::make_unique<SymbolSet>()), Ctx(VerifierContext{Diag}) {}

Result verify(GlobalRecord *R, const FrontendAttrs *FA);
Result verify(ObjCInterfaceRecord *R, const FrontendAttrs *FA);
Expand All @@ -77,6 +81,14 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
// Scan through dylib slices and report any remaining missing exports.
Result verifyRemainingSymbols();

/// Compare and report the attributes represented as
/// load commands in the dylib to the attributes provided via options.
bool verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
const BinaryAttrs &ProvidedBA,
const LibAttrs &ProvidedReexports,
const LibAttrs &ProvidedClients,
const LibAttrs &ProvidedRPaths, const FileType &FT);

/// Initialize target for verification.
void setTarget(const Target &T);

Expand Down Expand Up @@ -105,6 +117,10 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
bool shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
const Record *DR);

/// Check if declaration is exported from a reexported library. These
/// symbols should be omitted from the text-api file.
bool shouldIgnoreReexport(const Record *R, SymbolContext &SymCtx) const;

/// Compare the visibility declarations to the linkage of symbol found in
/// dylib.
Result compareVisibility(const Record *R, SymbolContext &SymCtx,
Expand Down Expand Up @@ -154,6 +170,9 @@ class DylibVerifier : llvm::MachO::RecordVisitor {
// Symbols in dylib.
llvm::MachO::Records Dylib;

// Reexported interfaces apart of the library.
ReexportedInterfaces Reexports;

// Controls what class of violations to report.
VerificationMode Mode = VerificationMode::Invalid;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/InstallAPI/MachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "llvm/TextAPI/TextAPIWriter.h"
#include "llvm/TextAPI/Utils.h"

using Architecture = llvm::MachO::Architecture;
using ArchitectureSet = llvm::MachO::ArchitectureSet;
using SymbolFlags = llvm::MachO::SymbolFlags;
using RecordLinkage = llvm::MachO::RecordLinkage;
using Record = llvm::MachO::Record;
Expand Down
313 changes: 56 additions & 257 deletions clang/include/clang/Sema/Sema.h

Large diffs are not rendered by default.

224 changes: 224 additions & 0 deletions clang/include/clang/Sema/SemaBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//===--- SemaBase.h - Common utilities for semantic analysis-----*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the SemaBase class, which provides utilities for Sema
// and its parts like SemaOpenACC.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_SEMA_SEMABASE_H
#define LLVM_CLANG_SEMA_SEMABASE_H

#include "clang/AST/Decl.h"
#include "clang/AST/Redeclarable.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Ownership.h"
#include "llvm/ADT/DenseMap.h"
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>

namespace clang {

class ASTContext;
class DiagnosticsEngine;
class LangOptions;
class Sema;

class SemaBase {
public:
SemaBase(Sema &S);

Sema &SemaRef;

ASTContext &getASTContext() const;
DiagnosticsEngine &getDiagnostics() const;
const LangOptions &getLangOpts() const;

/// Helper class that creates diagnostics with optional
/// template instantiation stacks.
///
/// This class provides a wrapper around the basic DiagnosticBuilder
/// class that emits diagnostics. ImmediateDiagBuilder is
/// responsible for emitting the diagnostic (as DiagnosticBuilder
/// does) and, if the diagnostic comes from inside a template
/// instantiation, printing the template instantiation stack as
/// well.
class ImmediateDiagBuilder : public DiagnosticBuilder {
Sema &SemaRef;
unsigned DiagID;

public:
ImmediateDiagBuilder(DiagnosticBuilder &DB, Sema &SemaRef, unsigned DiagID)
: DiagnosticBuilder(DB), SemaRef(SemaRef), DiagID(DiagID) {}
ImmediateDiagBuilder(DiagnosticBuilder &&DB, Sema &SemaRef, unsigned DiagID)
: DiagnosticBuilder(DB), SemaRef(SemaRef), DiagID(DiagID) {}

// This is a cunning lie. DiagnosticBuilder actually performs move
// construction in its copy constructor (but due to varied uses, it's not
// possible to conveniently express this as actual move construction). So
// the default copy ctor here is fine, because the base class disables the
// source anyway, so the user-defined ~ImmediateDiagBuilder is a safe no-op
// in that case anwyay.
ImmediateDiagBuilder(const ImmediateDiagBuilder &) = default;

~ImmediateDiagBuilder();

/// Teach operator<< to produce an object of the correct type.
template <typename T>
friend const ImmediateDiagBuilder &
operator<<(const ImmediateDiagBuilder &Diag, const T &Value) {
const DiagnosticBuilder &BaseDiag = Diag;
BaseDiag << Value;
return Diag;
}

// It is necessary to limit this to rvalue reference to avoid calling this
// function with a bitfield lvalue argument since non-const reference to
// bitfield is not allowed.
template <typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>>
const ImmediateDiagBuilder &operator<<(T &&V) const {
const DiagnosticBuilder &BaseDiag = *this;
BaseDiag << std::move(V);
return *this;
}
};

/// A generic diagnostic builder for errors which may or may not be deferred.
///
/// In CUDA, there exist constructs (e.g. variable-length arrays, try/catch)
/// which are not allowed to appear inside __device__ functions and are
/// allowed to appear in __host__ __device__ functions only if the host+device
/// function is never codegen'ed.
///
/// To handle this, we use the notion of "deferred diagnostics", where we
/// attach a diagnostic to a FunctionDecl that's emitted iff it's codegen'ed.
///
/// This class lets you emit either a regular diagnostic, a deferred
/// diagnostic, or no diagnostic at all, according to an argument you pass to
/// its constructor, thus simplifying the process of creating these "maybe
/// deferred" diagnostics.
class SemaDiagnosticBuilder {
public:
enum Kind {
/// Emit no diagnostics.
K_Nop,
/// Emit the diagnostic immediately (i.e., behave like Sema::Diag()).
K_Immediate,
/// Emit the diagnostic immediately, and, if it's a warning or error, also
/// emit a call stack showing how this function can be reached by an a
/// priori known-emitted function.
K_ImmediateWithCallStack,
/// Create a deferred diagnostic, which is emitted only if the function
/// it's attached to is codegen'ed. Also emit a call stack as with
/// K_ImmediateWithCallStack.
K_Deferred
};

SemaDiagnosticBuilder(Kind K, SourceLocation Loc, unsigned DiagID,
const FunctionDecl *Fn, Sema &S);
SemaDiagnosticBuilder(SemaDiagnosticBuilder &&D);
SemaDiagnosticBuilder(const SemaDiagnosticBuilder &) = default;

// The copy and move assignment operator is defined as deleted pending
// further motivation.
SemaDiagnosticBuilder &operator=(const SemaDiagnosticBuilder &) = delete;
SemaDiagnosticBuilder &operator=(SemaDiagnosticBuilder &&) = delete;

~SemaDiagnosticBuilder();

bool isImmediate() const { return ImmediateDiag.has_value(); }

/// Convertible to bool: True if we immediately emitted an error, false if
/// we didn't emit an error or we created a deferred error.
///
/// Example usage:
///
/// if (SemaDiagnosticBuilder(...) << foo << bar)
/// return ExprError();
///
/// But see CUDADiagIfDeviceCode() and CUDADiagIfHostCode() -- you probably
/// want to use these instead of creating a SemaDiagnosticBuilder yourself.
operator bool() const { return isImmediate(); }

template <typename T>
friend const SemaDiagnosticBuilder &
operator<<(const SemaDiagnosticBuilder &Diag, const T &Value) {
if (Diag.ImmediateDiag)
*Diag.ImmediateDiag << Value;
else if (Diag.PartialDiagId)
Diag.getDeviceDeferredDiags()[Diag.Fn][*Diag.PartialDiagId].second
<< Value;
return Diag;
}

// It is necessary to limit this to rvalue reference to avoid calling this
// function with a bitfield lvalue argument since non-const reference to
// bitfield is not allowed.
template <typename T,
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>>
const SemaDiagnosticBuilder &operator<<(T &&V) const {
if (ImmediateDiag)
*ImmediateDiag << std::move(V);
else if (PartialDiagId)
getDeviceDeferredDiags()[Fn][*PartialDiagId].second << std::move(V);
return *this;
}

friend const SemaDiagnosticBuilder &
operator<<(const SemaDiagnosticBuilder &Diag, const PartialDiagnostic &PD);

void AddFixItHint(const FixItHint &Hint) const;

friend ExprResult ExprError(const SemaDiagnosticBuilder &) {
return ExprError();
}
friend StmtResult StmtError(const SemaDiagnosticBuilder &) {
return StmtError();
}
operator ExprResult() const { return ExprError(); }
operator StmtResult() const { return StmtError(); }
operator TypeResult() const { return TypeError(); }
operator DeclResult() const { return DeclResult(true); }
operator MemInitResult() const { return MemInitResult(true); }

using DeferredDiagnosticsType =
llvm::DenseMap<CanonicalDeclPtr<const FunctionDecl>,
std::vector<PartialDiagnosticAt>>;

private:
Sema &S;
SourceLocation Loc;
unsigned DiagID;
const FunctionDecl *Fn;
bool ShowCallStack;

// Invariant: At most one of these Optionals has a value.
// FIXME: Switch these to a Variant once that exists.
std::optional<ImmediateDiagBuilder> ImmediateDiag;
std::optional<unsigned> PartialDiagId;

DeferredDiagnosticsType &getDeviceDeferredDiags() const;
};

/// Emit a diagnostic.
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID,
bool DeferHint = false);

/// Emit a partial diagnostic.
SemaDiagnosticBuilder Diag(SourceLocation Loc, const PartialDiagnostic &PD,
bool DeferHint = false);
};

} // namespace clang

#endif
14 changes: 2 additions & 12 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,14 @@
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/SemaBase.h"

namespace clang {

class ASTContext;
class DiagnosticEngine;
class LangOptions;
class Sema;

class SemaOpenACC {
class SemaOpenACC : public SemaBase {
public:
SemaOpenACC(Sema &S);

ASTContext &getASTContext() const;
DiagnosticsEngine &getDiagnostics() const;
const LangOptions &getLangOpts() const;

Sema &SemaRef;

/// Called after parsing an OpenACC Clause so that it can be checked.
bool ActOnClause(OpenACCClauseKind ClauseKind, SourceLocation StartLoc);

Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,15 @@ def PthreadLockChecker : Checker<"PthreadLock">,
def StreamChecker : Checker<"Stream">,
HelpText<"Check stream handling functions">,
WeakDependencies<[NonNullParamChecker]>,
CheckerOptions<[
CmdLineOption<Boolean,
"Pedantic",
"If false, assume that stream operations which are often not "
"checked for error do not fail."
"fail.",
"false",
InAlpha>
]>,
Documentation<HasDocumentation>;

def SimpleStreamChecker : Checker<"SimpleStream">,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2511,6 +2511,7 @@ unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src,
dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
assert(!P.getGlobal(VD));
assert(!Locals.contains(VD));
(void)VD;
}

// FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
Expand Down
8 changes: 0 additions & 8 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,6 @@ void TextNodeDumper::Visit(const OpenACCClause *C) {
{
ColorScope Color(OS, ShowColors, AttrColor);
OS << C->getClauseKind();

// Handle clauses with parens for types that have no children, likely
// because there is no sub expression.
switch (C->getClauseKind()) {
default:
// Nothing to do here.
break;
}
}
dumpPointer(C);
dumpSourceRange(SourceRange(C->getBeginLoc(), C->getEndLoc()));
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ void Environment::setValue(const Expr &E, Value &Val) {
if (auto *RecordVal = dyn_cast<RecordValue>(&Val)) {
assert(isOriginalRecordConstructor(CanonE) ||
&RecordVal->getLoc() == &getResultObjectLocation(CanonE));
(void)RecordVal;
}

assert(CanonE.isPRValue());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/AIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Path.h"

#include <set>

using AIX = clang::driver::toolchains::AIX;
using namespace clang::driver;
using namespace clang::driver::tools;
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,10 @@ CudaToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
}

for (Arg *A : Args) {
DAL->append(A);
// Make sure flags are not duplicated.
if (!llvm::is_contained(*DAL, A)) {
DAL->append(A);
}
}

if (!BoundArch.empty()) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ bool FormatTokenLexer::tryMergeNullishCoalescingEqual() {
return false;
auto &NullishCoalescing = *(Tokens.end() - 2);
auto &Equal = *(Tokens.end() - 1);
if (NullishCoalescing->getType() != TT_NullCoalescingOperator ||
if (NullishCoalescing->isNot(TT_NullCoalescingOperator) ||
Equal->isNot(tok::equal)) {
return false;
}
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Format/FormatTokenSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ class FormatTokenSource {
// getNextToken() -> a1
// getNextToken() -> a2
virtual FormatToken *insertTokens(ArrayRef<FormatToken *> Tokens) = 0;

[[nodiscard]] FormatToken *getNextNonComment() {
FormatToken *Tok;
do {
Tok = getNextToken();
assert(Tok);
} while (Tok->is(tok::comment));
return Tok;
}
};

class IndexedTokenSource : public FormatTokenSource {
Expand Down
10 changes: 4 additions & 6 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -825,8 +825,7 @@ class AnnotatingParser {
Parent->overwriteFixedType(TT_BinaryOperator);
}
// An arrow after an ObjC method expression is not a lambda arrow.
if (CurrentToken->getType() == TT_ObjCMethodExpr &&
CurrentToken->Next &&
if (CurrentToken->is(TT_ObjCMethodExpr) && CurrentToken->Next &&
CurrentToken->Next->is(TT_TrailingReturnArrow)) {
CurrentToken->Next->overwriteFixedType(TT_Unknown);
}
Expand Down Expand Up @@ -1563,7 +1562,7 @@ class AnnotatingParser {
case tok::l_brace:
if (Style.Language == FormatStyle::LK_TextProto) {
FormatToken *Previous = Tok->getPreviousNonComment();
if (Previous && Previous->getType() != TT_DictLiteral)
if (Previous && Previous->isNot(TT_DictLiteral))
Previous->setType(TT_SelectorName);
}
Scopes.push_back(getScopeType(*Tok));
Expand All @@ -1583,7 +1582,7 @@ class AnnotatingParser {
Tok->Previous->isOneOf(TT_SelectorName, TT_DictLiteral))) {
Tok->setType(TT_DictLiteral);
FormatToken *Previous = Tok->getPreviousNonComment();
if (Previous && Previous->getType() != TT_DictLiteral)
if (Previous && Previous->isNot(TT_DictLiteral))
Previous->setType(TT_SelectorName);
}
if (Style.isTableGen())
Expand Down Expand Up @@ -4754,8 +4753,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
// Objective-C dictionary literal -> no space before closing brace.
return false;
}
if (Right.getType() == TT_TrailingAnnotation &&
Right.isOneOf(tok::amp, tok::ampamp) &&
if (Right.is(TT_TrailingAnnotation) && Right.isOneOf(tok::amp, tok::ampamp) &&
Left.isOneOf(tok::kw_const, tok::kw_volatile) &&
(!Right.Next || Right.Next->is(tok::semi))) {
// Match const and volatile ref-qualifiers without any additional
Expand Down
38 changes: 15 additions & 23 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,11 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
nextToken();
continue;
}
tok::TokenKind kind = FormatTok->Tok.getKind();
if (FormatTok->getType() == TT_MacroBlockBegin)
kind = tok::l_brace;
else if (FormatTok->getType() == TT_MacroBlockEnd)
kind = tok::r_brace;
tok::TokenKind Kind = FormatTok->Tok.getKind();
if (FormatTok->is(TT_MacroBlockBegin))
Kind = tok::l_brace;
else if (FormatTok->is(TT_MacroBlockEnd))
Kind = tok::r_brace;

auto ParseDefault = [this, OpeningBrace, IfKind, &IfLBrace, &HasDoWhile,
&HasLabel, &StatementCount] {
Expand All @@ -380,7 +380,7 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
assert(StatementCount > 0 && "StatementCount overflow!");
};

switch (kind) {
switch (Kind) {
case tok::comment:
nextToken();
addUnwrappedLine();
Expand Down Expand Up @@ -427,11 +427,7 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
break;
case tok::kw_default: {
unsigned StoredPosition = Tokens->getPosition();
FormatToken *Next;
do {
Next = Tokens->getNextToken();
assert(Next);
} while (Next->is(tok::comment));
auto *Next = Tokens->getNextNonComment();
FormatTok = Tokens->setPosition(StoredPosition);
if (Next->isNot(tok::colon)) {
// default not followed by ':' is not a case label; treat it like
Expand Down Expand Up @@ -497,10 +493,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
assert(Tok->is(tok::l_brace));

do {
FormatToken *NextTok;
do {
NextTok = Tokens->getNextToken();
} while (NextTok->is(tok::comment));
auto *NextTok = Tokens->getNextNonComment();

if (!Line->InMacroBody && !Style.isTableGen()) {
// Skip PPDirective lines and comments.
Expand Down Expand Up @@ -3280,8 +3273,8 @@ void UnwrappedLineParser::parseSwitch() {
}

// Operators that can follow a C variable.
static bool isCOperatorFollowingVar(tok::TokenKind kind) {
switch (kind) {
static bool isCOperatorFollowingVar(tok::TokenKind Kind) {
switch (Kind) {
case tok::ampamp:
case tok::ampequal:
case tok::arrow:
Expand Down Expand Up @@ -4709,14 +4702,13 @@ void UnwrappedLineParser::readToken(int LevelDifference) {
do {
FormatTok = Tokens->getNextToken();
assert(FormatTok);
while (FormatTok->getType() == TT_ConflictStart ||
FormatTok->getType() == TT_ConflictEnd ||
FormatTok->getType() == TT_ConflictAlternative) {
if (FormatTok->getType() == TT_ConflictStart)
while (FormatTok->isOneOf(TT_ConflictStart, TT_ConflictEnd,
TT_ConflictAlternative)) {
if (FormatTok->is(TT_ConflictStart))
conditionalCompilationStart(/*Unreachable=*/false);
else if (FormatTok->getType() == TT_ConflictAlternative)
else if (FormatTok->is(TT_ConflictAlternative))
conditionalCompilationAlternative();
else if (FormatTok->getType() == TT_ConflictEnd)
else if (FormatTok->is(TT_ConflictEnd))
conditionalCompilationEnd();
FormatTok = Tokens->getNextToken();
FormatTok->MustBreakBefore = true;
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,7 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
Style.ReferenceAlignment != FormatStyle::RAS_Right &&
Style.ReferenceAlignment != FormatStyle::RAS_Pointer;
for (int Previous = i - 1;
Previous >= 0 &&
Changes[Previous].Tok->getType() == TT_PointerOrReference;
Previous >= 0 && Changes[Previous].Tok->is(TT_PointerOrReference);
--Previous) {
assert(Changes[Previous].Tok->isPointerOrReference());
if (Changes[Previous].Tok->isNot(tok::star)) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/InstallAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
)

add_clang_library(clangInstallAPI
DiagnosticBuilderWrappers.cpp
DylibVerifier.cpp
FileList.cpp
Frontend.cpp
Expand Down
110 changes: 110 additions & 0 deletions clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//===- DiagnosticBuilderWrappers.cpp ----------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "DiagnosticBuilderWrappers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TextAPI/Platform.h"

using clang::DiagnosticBuilder;

namespace llvm {
namespace MachO {
const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const Architecture &Arch) {
DB.AddString(getArchitectureName(Arch));
return DB;
}

const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const ArchitectureSet &ArchSet) {
DB.AddString(std::string(ArchSet));
return DB;
}

const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PlatformType &Platform) {
DB.AddString(getPlatformName(Platform));
return DB;
}

const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PlatformVersionSet &Platforms) {
std::string PlatformAsString;
raw_string_ostream Stream(PlatformAsString);

Stream << "[ ";
llvm::interleaveComma(
Platforms, Stream,
[&Stream](const std::pair<PlatformType, VersionTuple> &PV) {
Stream << getPlatformName(PV.first);
if (!PV.second.empty())
Stream << PV.second.getAsString();
});
Stream << " ]";
DB.AddString(Stream.str());
return DB;
}

const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const FileType &Type) {
switch (Type) {
case FileType::MachO_Bundle:
DB.AddString("mach-o bundle");
return DB;
case FileType::MachO_DynamicLibrary:
DB.AddString("mach-o dynamic library");
return DB;
case FileType::MachO_DynamicLibrary_Stub:
DB.AddString("mach-o dynamic library stub");
return DB;
case FileType::TBD_V1:
DB.AddString("tbd-v1");
return DB;
case FileType::TBD_V2:
DB.AddString("tbd-v2");
return DB;
case FileType::TBD_V3:
DB.AddString("tbd-v3");
return DB;
case FileType::TBD_V4:
DB.AddString("tbd-v4");
return DB;
case FileType::TBD_V5:
DB.AddString("tbd-v5");
return DB;
case FileType::Invalid:
case FileType::All:
break;
}
llvm_unreachable("Unexpected file type for diagnostics.");
}

const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
const PackedVersion &Version) {
std::string VersionString;
raw_string_ostream OS(VersionString);
OS << Version;
DB.AddString(OS.str());
return DB;
}

const clang::DiagnosticBuilder &
operator<<(const clang::DiagnosticBuilder &DB,
const StringMapEntry<ArchitectureSet> &LibAttr) {
std::string IFAsString;
raw_string_ostream OS(IFAsString);

OS << LibAttr.getKey() << " [ " << LibAttr.getValue() << " ]";
DB.AddString(OS.str());
return DB;
}

} // namespace MachO
} // namespace llvm
49 changes: 49 additions & 0 deletions clang/lib/InstallAPI/DiagnosticBuilderWrappers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===- DiagnosticBuilderWrappers.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
//
//===----------------------------------------------------------------------===//
//
/// Diagnostic wrappers for TextAPI types for error reporting.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H
#define LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H

#include "clang/Basic/Diagnostic.h"
#include "llvm/TextAPI/Architecture.h"
#include "llvm/TextAPI/ArchitectureSet.h"
#include "llvm/TextAPI/InterfaceFile.h"
#include "llvm/TextAPI/Platform.h"

namespace llvm {
namespace MachO {

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PlatformType &Platform);

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PlatformVersionSet &Platforms);

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const Architecture &Arch);

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const ArchitectureSet &ArchSet);

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const FileType &Type);

const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
const PackedVersion &Version);

const clang::DiagnosticBuilder &
operator<<(const clang::DiagnosticBuilder &DB,
const StringMapEntry<ArchitectureSet> &LibAttr);

} // namespace MachO
} // namespace llvm
#endif // LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H
196 changes: 196 additions & 0 deletions clang/lib/InstallAPI/DylibVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "clang/InstallAPI/DylibVerifier.h"
#include "DiagnosticBuilderWrappers.h"
#include "clang/InstallAPI/FrontendRecords.h"
#include "clang/InstallAPI/InstallAPIDiagnostic.h"
#include "llvm/Demangle/Demangle.h"
Expand Down Expand Up @@ -178,6 +179,22 @@ bool DylibVerifier::shouldIgnoreObsolete(const Record *R, SymbolContext &SymCtx,
return SymCtx.FA->Avail.isObsoleted();
}

bool DylibVerifier::shouldIgnoreReexport(const Record *R,
SymbolContext &SymCtx) const {
if (Reexports.empty())
return false;

for (const InterfaceFile &Lib : Reexports) {
if (!Lib.hasTarget(Ctx.Target))
continue;
if (auto Sym =
Lib.getSymbol(SymCtx.Kind, SymCtx.SymbolName, SymCtx.ObjCIFKind))
if ((*Sym)->hasTarget(Ctx.Target))
return true;
}
return false;
}

bool DylibVerifier::compareObjCInterfaceSymbols(const Record *R,
SymbolContext &SymCtx,
const ObjCInterfaceRecord *DR) {
Expand Down Expand Up @@ -383,6 +400,11 @@ DylibVerifier::Result DylibVerifier::verifyImpl(Record *R,
return Ctx.FrontendState;
}

if (shouldIgnoreReexport(R, SymCtx)) {
updateState(Result::Ignore);
return Ctx.FrontendState;
}

Record *DR =
findRecordFromSlice(Ctx.DylibSlice, SymCtx.SymbolName, SymCtx.Kind);
if (DR)
Expand Down Expand Up @@ -702,5 +724,179 @@ DylibVerifier::Result DylibVerifier::verifyRemainingSymbols() {
return getState();
}

bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
const BinaryAttrs &ProvidedBA,
const LibAttrs &ProvidedReexports,
const LibAttrs &ProvidedClients,
const LibAttrs &ProvidedRPaths,
const FileType &FT) {
assert(!Dylib.empty() && "Need dylib to verify.");

// Pickup any load commands that can differ per slice to compare.
TargetList DylibTargets;
LibAttrs DylibReexports;
LibAttrs DylibClients;
LibAttrs DylibRPaths;
for (const std::shared_ptr<RecordsSlice> &RS : Dylib) {
DylibTargets.push_back(RS->getTarget());
const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
for (const StringRef LibName : BinInfo.RexportedLibraries)
DylibReexports[LibName].set(DylibTargets.back().Arch);
for (const StringRef LibName : BinInfo.AllowableClients)
DylibClients[LibName].set(DylibTargets.back().Arch);
// Compare attributes that are only representable in >= TBD_V5.
if (FT >= FileType::TBD_V5)
for (const StringRef Name : BinInfo.RPaths)
DylibRPaths[Name].set(DylibTargets.back().Arch);
}

// Check targets first.
ArchitectureSet ProvidedArchs = mapToArchitectureSet(ProvidedTargets);
ArchitectureSet DylibArchs = mapToArchitectureSet(DylibTargets);
if (ProvidedArchs != DylibArchs) {
Ctx.Diag->Report(diag::err_architecture_mismatch)
<< ProvidedArchs << DylibArchs;
return false;
}
auto ProvidedPlatforms = mapToPlatformVersionSet(ProvidedTargets);
auto DylibPlatforms = mapToPlatformVersionSet(DylibTargets);
if (ProvidedPlatforms != DylibPlatforms) {
const bool DiffMinOS =
mapToPlatformSet(ProvidedTargets) == mapToPlatformSet(DylibTargets);
if (DiffMinOS)
Ctx.Diag->Report(diag::warn_platform_mismatch)
<< ProvidedPlatforms << DylibPlatforms;
else {
Ctx.Diag->Report(diag::err_platform_mismatch)
<< ProvidedPlatforms << DylibPlatforms;
return false;
}
}

// Because InstallAPI requires certain attributes to match across architecture
// slices, take the first one to compare those with.
const BinaryAttrs &DylibBA = (*Dylib.begin())->getBinaryAttrs();

if (ProvidedBA.InstallName != DylibBA.InstallName) {
Ctx.Diag->Report(diag::err_install_name_mismatch)
<< ProvidedBA.InstallName << DylibBA.InstallName;
return false;
}

if (ProvidedBA.CurrentVersion != DylibBA.CurrentVersion) {
Ctx.Diag->Report(diag::err_current_version_mismatch)
<< ProvidedBA.CurrentVersion << DylibBA.CurrentVersion;
return false;
}

if (ProvidedBA.CompatVersion != DylibBA.CompatVersion) {
Ctx.Diag->Report(diag::err_compatibility_version_mismatch)
<< ProvidedBA.CompatVersion << DylibBA.CompatVersion;
return false;
}

if (ProvidedBA.AppExtensionSafe != DylibBA.AppExtensionSafe) {
Ctx.Diag->Report(diag::err_appextension_safe_mismatch)
<< (ProvidedBA.AppExtensionSafe ? "true" : "false")
<< (DylibBA.AppExtensionSafe ? "true" : "false");
return false;
}

if (!DylibBA.TwoLevelNamespace) {
Ctx.Diag->Report(diag::err_no_twolevel_namespace);
return false;
}

if (ProvidedBA.OSLibNotForSharedCache != DylibBA.OSLibNotForSharedCache) {
Ctx.Diag->Report(diag::err_shared_cache_eligiblity_mismatch)
<< (ProvidedBA.OSLibNotForSharedCache ? "true" : "false")
<< (DylibBA.OSLibNotForSharedCache ? "true" : "false");
return false;
}

if (ProvidedBA.ParentUmbrella.empty() && !DylibBA.ParentUmbrella.empty()) {
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
<< "installAPI option" << DylibBA.ParentUmbrella;
return false;
}

if (!ProvidedBA.ParentUmbrella.empty() && DylibBA.ParentUmbrella.empty()) {
Ctx.Diag->Report(diag::err_parent_umbrella_missing)
<< "binary file" << ProvidedBA.ParentUmbrella;
return false;
}

if ((!ProvidedBA.ParentUmbrella.empty()) &&
(ProvidedBA.ParentUmbrella != DylibBA.ParentUmbrella)) {
Ctx.Diag->Report(diag::err_parent_umbrella_mismatch)
<< ProvidedBA.ParentUmbrella << DylibBA.ParentUmbrella;
return false;
}

auto CompareLibraries = [&](const LibAttrs &Provided, const LibAttrs &Dylib,
unsigned DiagID_missing, unsigned DiagID_mismatch,
bool Fatal = true) {
if (Provided == Dylib)
return true;

for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
const auto DAttrIt = Dylib.find(PAttr.getKey());
if (DAttrIt == Dylib.end()) {
Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
if (Fatal)
return false;
}

if (PAttr.getValue() != DAttrIt->getValue()) {
Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
if (Fatal)
return false;
}
}

for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
const auto PAttrIt = Provided.find(DAttr.getKey());
if (PAttrIt == Provided.end()) {
Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
if (!Fatal)
continue;
return false;
}

if (PAttrIt->getValue() != DAttr.getValue()) {
if (Fatal)
llvm_unreachable("this case was already covered above.");
}
}
return true;
};

if (!CompareLibraries(ProvidedReexports, DylibReexports,
diag::err_reexported_libraries_missing,
diag::err_reexported_libraries_mismatch))
return false;

if (!CompareLibraries(ProvidedClients, DylibClients,
diag::err_allowable_clients_missing,
diag::err_allowable_clients_mismatch))
return false;

if (FT >= FileType::TBD_V5) {
// Ignore rpath differences if building an asan variant, since the
// compiler injects additional paths.
// FIXME: Building with sanitizers does not always change the install
// name, so this is not a foolproof solution.
if (!ProvidedBA.InstallName.ends_with("_asan")) {
if (!CompareLibraries(ProvidedRPaths, DylibRPaths,
diag::warn_rpaths_missing,
diag::warn_rpaths_mismatch,
/*Fatal=*/false))
return true;
}
}

return true;
}

} // namespace installapi
} // namespace clang
54 changes: 54 additions & 0 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,58 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
return llvm::MemoryBuffer::getMemBufferCopy(Contents, BufferName);
}

std::string findLibrary(StringRef InstallName, FileManager &FM,
ArrayRef<std::string> FrameworkSearchPaths,
ArrayRef<std::string> LibrarySearchPaths,
ArrayRef<std::string> SearchPaths) {
auto getLibrary =
[&](const StringRef FullPath) -> std::optional<std::string> {
// Prefer TextAPI files when possible.
SmallString<PATH_MAX> TextAPIFilePath = FullPath;
replace_extension(TextAPIFilePath, ".tbd");

if (FM.getOptionalFileRef(TextAPIFilePath))
return std::string(TextAPIFilePath);

if (FM.getOptionalFileRef(FullPath))
return std::string(FullPath);

return std::nullopt;
};

const StringRef Filename = sys::path::filename(InstallName);
const bool IsFramework = sys::path::parent_path(InstallName)
.ends_with((Filename + ".framework").str());
if (IsFramework) {
for (const StringRef Path : FrameworkSearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename + StringRef(".framework"), Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
} else {
// Copy Apple's linker behavior: If this is a .dylib inside a framework, do
// not search -L paths.
bool IsEmbeddedDylib = (sys::path::extension(InstallName) == ".dylib") &&
InstallName.contains(".framework/");
if (!IsEmbeddedDylib) {
for (const StringRef Path : LibrarySearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, Filename);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}
}
}

for (const StringRef Path : SearchPaths) {
SmallString<PATH_MAX> FullPath(Path);
sys::path::append(FullPath, InstallName);
if (auto LibOrNull = getLibrary(FullPath))
return *LibOrNull;
}

return {};
}

} // namespace clang::installapi
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_clang_library(clangSema
SemaAttr.cpp
SemaAPINotes.cpp
SemaAvailability.cpp
SemaBase.cpp
SemaCXXScopeSpec.cpp
SemaCast.cpp
SemaChecking.cpp
Expand Down
45 changes: 9 additions & 36 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,15 @@ const uint64_t Sema::MaximumAlignment;

Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter)
: CollectStats(false), TUKind(TUKind), CurFPFeatures(pp.getLangOpts()),
LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer),
Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
APINotes(SourceMgr, LangOpts), AnalysisWarnings(*this),
ThreadSafetyDeclCache(nullptr), LateTemplateParser(nullptr),
LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr),
CurContext(nullptr), ExternalSource(nullptr), CurScope(nullptr),
Ident_super(nullptr), OpenACCPtr(std::make_unique<SemaOpenACC>(*this)),
: SemaBase(*this), CollectStats(false), TUKind(TUKind),
CurFPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
SourceMgr(PP.getSourceManager()), APINotes(SourceMgr, LangOpts),
AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
CurScope(nullptr), Ident_super(nullptr),
OpenACCPtr(std::make_unique<SemaOpenACC>(*this)),
MSPointerToMemberRepresentationMethod(
LangOpts.getMSPointerToMemberRepresentationMethod()),
MSStructPragmaOn(false), VtorDispStack(LangOpts.getVtorDispMode()),
Expand Down Expand Up @@ -1612,11 +1613,6 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
PrintContextStack();
}

Sema::SemaDiagnosticBuilder
Sema::Diag(SourceLocation Loc, const PartialDiagnostic &PD, bool DeferHint) {
return Diag(Loc, PD.getDiagID(), DeferHint) << PD;
}

bool Sema::hasUncompilableErrorOccurred() const {
if (getDiagnostics().hasUncompilableErrorOccurred())
return true;
Expand Down Expand Up @@ -1911,29 +1907,6 @@ Sema::targetDiag(SourceLocation Loc, unsigned DiagID, const FunctionDecl *FD) {
FD, *this);
}

Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID,
bool DeferHint) {
bool IsError = Diags.getDiagnosticIDs()->isDefaultMappingAsError(DiagID);
bool ShouldDefer = getLangOpts().CUDA && LangOpts.GPUDeferDiag &&
DiagnosticIDs::isDeferrable(DiagID) &&
(DeferHint || DeferDiags || !IsError);
auto SetIsLastErrorImmediate = [&](bool Flag) {
if (IsError)
IsLastErrorImmediate = Flag;
};
if (!ShouldDefer) {
SetIsLastErrorImmediate(true);
return SemaDiagnosticBuilder(SemaDiagnosticBuilder::K_Immediate, Loc,
DiagID, getCurFunctionDecl(), *this);
}

SemaDiagnosticBuilder DB = getLangOpts().CUDAIsDevice
? CUDADiagIfDeviceCode(Loc, DiagID)
: CUDADiagIfHostCode(Loc, DiagID);
SetIsLastErrorImmediate(DB.isImmediate());
return DB;
}

void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
if (isUnevaluatedContext() || Ty.isNull())
return;
Expand Down
85 changes: 85 additions & 0 deletions clang/lib/Sema/SemaBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "clang/Sema/SemaBase.h"
#include "clang/Sema/Sema.h"

namespace clang {

SemaBase::SemaBase(Sema &S) : SemaRef(S) {}

ASTContext &SemaBase::getASTContext() const { return SemaRef.Context; }
DiagnosticsEngine &SemaBase::getDiagnostics() const { return SemaRef.Diags; }
const LangOptions &SemaBase::getLangOpts() const { return SemaRef.LangOpts; }

SemaBase::ImmediateDiagBuilder::~ImmediateDiagBuilder() {
// If we aren't active, there is nothing to do.
if (!isActive())
return;

// Otherwise, we need to emit the diagnostic. First clear the diagnostic
// builder itself so it won't emit the diagnostic in its own destructor.
//
// This seems wasteful, in that as written the DiagnosticBuilder dtor will
// do its own needless checks to see if the diagnostic needs to be
// emitted. However, because we take care to ensure that the builder
// objects never escape, a sufficiently smart compiler will be able to
// eliminate that code.
Clear();

// Dispatch to Sema to emit the diagnostic.
SemaRef.EmitCurrentDiagnostic(DiagID);
}

const SemaBase::SemaDiagnosticBuilder &
operator<<(const SemaBase::SemaDiagnosticBuilder &Diag,
const PartialDiagnostic &PD) {
if (Diag.ImmediateDiag)
PD.Emit(*Diag.ImmediateDiag);
else if (Diag.PartialDiagId)
Diag.S.DeviceDeferredDiags[Diag.Fn][*Diag.PartialDiagId].second = PD;
return Diag;
}

void SemaBase::SemaDiagnosticBuilder::AddFixItHint(
const FixItHint &Hint) const {
if (ImmediateDiag)
ImmediateDiag->AddFixItHint(Hint);
else if (PartialDiagId)
S.DeviceDeferredDiags[Fn][*PartialDiagId].second.AddFixItHint(Hint);
}

llvm::DenseMap<CanonicalDeclPtr<const FunctionDecl>,
std::vector<PartialDiagnosticAt>> &
SemaBase::SemaDiagnosticBuilder::getDeviceDeferredDiags() const {
return S.DeviceDeferredDiags;
}

Sema::SemaDiagnosticBuilder SemaBase::Diag(SourceLocation Loc, unsigned DiagID,
bool DeferHint) {
bool IsError =
getDiagnostics().getDiagnosticIDs()->isDefaultMappingAsError(DiagID);
bool ShouldDefer = getLangOpts().CUDA && getLangOpts().GPUDeferDiag &&
DiagnosticIDs::isDeferrable(DiagID) &&
(DeferHint || SemaRef.DeferDiags || !IsError);
auto SetIsLastErrorImmediate = [&](bool Flag) {
if (IsError)
SemaRef.IsLastErrorImmediate = Flag;
};
if (!ShouldDefer) {
SetIsLastErrorImmediate(true);
return SemaDiagnosticBuilder(SemaDiagnosticBuilder::K_Immediate, Loc,
DiagID, SemaRef.getCurFunctionDecl(), SemaRef);
}

SemaDiagnosticBuilder DB = getLangOpts().CUDAIsDevice
? SemaRef.CUDADiagIfDeviceCode(Loc, DiagID)
: SemaRef.CUDADiagIfHostCode(Loc, DiagID);
SetIsLastErrorImmediate(DB.isImmediate());
return DB;
}

Sema::SemaDiagnosticBuilder SemaBase::Diag(SourceLocation Loc,
const PartialDiagnostic &PD,
bool DeferHint) {
return Diag(Loc, PD.getDiagID(), DeferHint) << PD;
}

} // namespace clang
Loading