in lib/Sema/MiscDiagnostics.cpp [105:1327]
static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
bool isExprStmt) {
class DiagnoseWalker : public BaseDiagnosticWalker {
SmallPtrSet<Expr*, 4> AlreadyDiagnosedMetatypes;
SmallPtrSet<DeclRefExpr*, 4> AlreadyDiagnosedBitCasts;
bool IsExprStmt;
public:
ASTContext &Ctx;
const DeclContext *DC;
DiagnoseWalker(const DeclContext *DC, bool isExprStmt)
: IsExprStmt(isExprStmt), Ctx(DC->getASTContext()), DC(DC) {}
std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override {
return { false, P };
}
bool walkToTypeReprPre(TypeRepr *T) override { return true; }
bool shouldWalkCaptureInitializerExpressions() override { return true; }
bool shouldWalkIntoTapExpression() override { return false; }
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
// See through implicit conversions of the expression. We want to be able
// to associate the parent of this expression with the ultimate callee.
auto Base = E;
while (auto Conv = dyn_cast<ImplicitConversionExpr>(Base))
Base = Conv->getSubExpr();
if (auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
// Verify metatype uses.
if (isa<TypeDecl>(DRE->getDecl())) {
if (isa<ModuleDecl>(DRE->getDecl()))
checkUseOfModule(DRE);
else
checkUseOfMetaTypeName(Base);
}
// Verify warn_unqualified_access uses.
checkUnqualifiedAccessUse(DRE);
// Verify that special decls are eliminated.
checkForDeclWithSpecialTypeCheckingSemantics(DRE);
// Verify that `unsafeBitCast` isn't misused.
checkForSuspiciousBitCasts(DRE, nullptr);
}
if (auto *MRE = dyn_cast<MemberRefExpr>(Base)) {
if (isa<TypeDecl>(MRE->getMember().getDecl()))
checkUseOfMetaTypeName(Base);
}
if (isa<TypeExpr>(Base))
checkUseOfMetaTypeName(Base);
if (auto *KPE = dyn_cast<KeyPathExpr>(E)) {
// raise an error if this KeyPath contains an effectful member.
checkForEffectfulKeyPath(KPE);
}
// Check function calls, looking through implicit conversions on the
// function and inspecting the arguments directly.
if (auto *Call = dyn_cast<ApplyExpr>(E)) {
// Warn about surprising implicit optional promotions.
checkOptionalPromotions(Call);
// Check the callee, looking through implicit conversions.
auto base = Call->getFn();
unsigned uncurryLevel = 0;
while (auto conv = dyn_cast<ImplicitConversionExpr>(base))
base = conv->getSubExpr();
const auto findDynamicMemberRefExpr =
[](Expr *e) -> DynamicMemberRefExpr* {
if (auto open = dyn_cast<OpenExistentialExpr>(e)) {
return dyn_cast<DynamicMemberRefExpr>(open->getSubExpr());
}
return nullptr;
};
if (auto force = dyn_cast<ForceValueExpr>(base)) {
if (auto ref = findDynamicMemberRefExpr(force->getSubExpr()))
base = ref;
} else if (auto bind = dyn_cast<BindOptionalExpr>(base)) {
if (auto ref = findDynamicMemberRefExpr(bind->getSubExpr()))
base = ref;
}
while (auto ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(base))
base = ignoredBase->getRHS();
ConcreteDeclRef callee;
if (auto *calleeDRE = dyn_cast<DeclRefExpr>(base)) {
checkForSuspiciousBitCasts(calleeDRE, Call);
callee = calleeDRE->getDeclRef();
// Otherwise, try to drill down through member calls for the purposes
// of argument-matching code below.
} else if (auto selfApply = dyn_cast<SelfApplyExpr>(base)) {
++uncurryLevel;
base = selfApply->getSemanticFn();
if (auto calleeDRE = dyn_cast<DeclRefExpr>(base))
callee = calleeDRE->getDeclRef();
// Otherwise, check for a dynamic member.
} else if (auto dynamicMRE = dyn_cast<DynamicMemberRefExpr>(base)) {
++uncurryLevel;
callee = dynamicMRE->getMember();
}
if (callee) {
auto *args = Call->getArgs();
for (auto idx : indices(*args)) {
auto *arg = args->getExpr(idx);
checkMagicIdentifierMismatch(callee, uncurryLevel, idx, arg);
// InOutExprs can be wrapped in some implicit casts.
Expr *unwrapped = arg;
if (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(arg))
unwrapped = IIO->getSubExpr();
if (isa<InOutToPointerExpr>(unwrapped) ||
isa<ArrayToPointerExpr>(unwrapped) ||
isa<ErasureExpr>(unwrapped)) {
auto operand =
cast<ImplicitConversionExpr>(unwrapped)->getSubExpr();
if (auto *IOE = dyn_cast<InOutExpr>(operand))
operand = IOE->getSubExpr();
// Also do some additional work based on how the function uses
// the argument.
checkConvertedPointerArgument(callee, uncurryLevel, idx,
unwrapped, operand);
}
}
}
}
// If we have an assignment expression, scout ahead for acceptable _'s.
if (auto *AE = dyn_cast<AssignExpr>(E)) {
auto destExpr = AE->getDest();
// If the user is assigning the result of a function that returns
// Void to _ then warn, because that is redundant.
if (auto DAE = dyn_cast<DiscardAssignmentExpr>(destExpr)) {
if (auto CE = dyn_cast<CallExpr>(AE->getSrc())) {
if (isa_and_nonnull<FuncDecl>(CE->getCalledValue()) &&
CE->getType()->isVoid()) {
Ctx.Diags
.diagnose(DAE->getLoc(),
diag::discard_expr_void_result_redundant)
.fixItRemoveChars(DAE->getStartLoc(),
AE->getSrc()->getStartLoc());
}
}
}
}
// Diagnose 'self.init' or 'super.init' nested in another expression
// or closure.
if (auto *rebindSelfExpr = dyn_cast<RebindSelfInConstructorExpr>(E)) {
if (!Parent.isNull() || !IsExprStmt || DC->getParent()->isLocalContext()) {
bool isChainToSuper;
(void)rebindSelfExpr->getCalledConstructor(isChainToSuper);
Ctx.Diags.diagnose(E->getLoc(), diag::init_delegation_nested,
isChainToSuper, !IsExprStmt);
}
}
// Diagnose single-element tuple expressions.
if (auto *tupleExpr = dyn_cast<TupleExpr>(E)) {
if (tupleExpr->getNumElements() == 1) {
Ctx.Diags.diagnose(tupleExpr->getElementNameLoc(0),
diag::tuple_single_element)
.fixItRemoveChars(tupleExpr->getElementNameLoc(0),
tupleExpr->getElement(0)->getStartLoc());
}
}
auto diagnoseDuplicateLabels = [&](SourceLoc loc,
ArrayRef<Identifier> labels) {
llvm::SmallDenseSet<Identifier> names;
names.reserve(labels.size());
for (auto name : labels) {
if (name.empty())
continue;
auto inserted = names.insert(name).second;
if (!inserted) {
Ctx.Diags.diagnose(loc, diag::tuple_duplicate_label);
return;
}
}
};
// FIXME: Duplicate labels on enum payloads should be diagnosed
// when declared, not when called.
if (auto *CE = dyn_cast_or_null<CallExpr>(E)) {
auto calledValue = CE->getCalledValue();
if (calledValue && isa<EnumElementDecl>(calledValue)) {
auto *args = CE->getArgs();
SmallVector<Identifier, 4> scratch;
diagnoseDuplicateLabels(args->getLoc(),
args->getArgumentLabels(scratch));
}
}
// Diagnose tuple expressions with duplicate element label.
if (auto *tupleExpr = dyn_cast<TupleExpr>(E)) {
diagnoseDuplicateLabels(tupleExpr->getLoc(),
tupleExpr->getElementNames());
}
// Diagnose checked casts that involve marker protocols.
if (auto cast = dyn_cast<CheckedCastExpr>(E)) {
checkCheckedCastExpr(cast);
}
return { true, E };
}
/// Visit each component of the keypath and emit a diganostic if they
/// refer to a member that has effects.
void checkForEffectfulKeyPath(KeyPathExpr *keyPath) {
for (const auto &component : keyPath->getComponents()) {
if (component.hasDeclRef()) {
auto decl = component.getDeclRef().getDecl();
if (auto asd = dyn_cast<AbstractStorageDecl>(decl)) {
if (auto getter = asd->getEffectfulGetAccessor()) {
Ctx.Diags.diagnose(component.getLoc(),
diag::effectful_keypath_component,
asd->getDescriptiveKind());
Ctx.Diags.diagnose(asd->getLoc(), diag::kind_declared_here,
asd->getDescriptiveKind());
}
}
}
}
}
void checkCheckedCastExpr(CheckedCastExpr *cast) {
if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))
return;
Type castType = cast->getCastType();
if (!castType || !castType->isExistentialType())
return;
auto layout = castType->getExistentialLayout();
for (auto proto : layout.getProtocols()) {
if (proto->getDecl()->isMarkerProtocol()) {
Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
proto->getDecl()->getName());
}
}
}
static Expr *lookThroughArgument(Expr *arg) {
while (1) {
if (auto conv = dyn_cast<ImplicitConversionExpr>(arg))
arg = conv->getSubExpr();
else if (auto *PE = dyn_cast<ParenExpr>(arg))
arg = PE->getSubExpr();
else
break;
}
return arg;
}
void checkConvertedPointerArgument(ConcreteDeclRef callee,
unsigned uncurryLevel,
unsigned argIndex,
Expr *pointerExpr,
Expr *storage) {
if (!isPointerIdentityArgument(callee, uncurryLevel, argIndex))
return;
// Flag that the argument is non-accessing.
if (auto inout = dyn_cast<InOutToPointerExpr>(pointerExpr)) {
inout->setNonAccessing(true);
} else if (auto array = dyn_cast<ArrayToPointerExpr>(pointerExpr)) {
array->setNonAccessing(true);
}
// TODO: warn if taking the address of 'storage' will definitely
// yield a temporary address.
}
/// Is the given call argument, known to be of pointer type, just used
/// for its pointer identity?
bool isPointerIdentityArgument(ConcreteDeclRef ref, unsigned uncurryLevel,
unsigned argIndex) {
// FIXME: derive this from an attribute instead of hacking it based
// on the target name!
auto decl = ref.getDecl();
// Assume that == and != are non-accessing uses.
if (decl->isOperator()) {
auto op = decl->getBaseName();
if (op == "==" || op == "!=")
return true;
return false;
}
// NSObject.addObserver(_:forKeyPath:options:context:)
if (uncurryLevel == 1 && argIndex == 3) {
return decl->getName().isCompoundName("addObserver",
{ "", "forKeyPath",
"options", "context" });
}
// NSObject.removeObserver(_:forKeyPath:context:)
if (uncurryLevel == 1 && argIndex == 2) {
return decl->getName().isCompoundName("removeObserver",
{ "", "forKeyPath", "context" });
}
return false;
}
/// We have a collection literal with a defaulted type, e.g. of [Any]. Emit
/// an error if it was inferred to this type in an invalid context, which is
/// one in which the parent expression is not itself a collection literal.
void checkTypeDefaultedCollectionExpr(CollectionExpr *c) {
// If the parent is a non-expression, or is not itself a literal, then
// produce an error with a fixit to add the type as an explicit
// annotation.
if (c->getNumElements() == 0)
Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_empty)
.highlight(c->getSourceRange());
else {
assert(c->getType()->hasTypeRepr() &&
"a defaulted type should always be printable");
Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_heterogeneous,
c->getType())
.highlight(c->getSourceRange())
.fixItInsertAfter(c->getEndLoc(), " as " + c->getType()->getString());
}
}
void checkMagicIdentifierMismatch(ConcreteDeclRef callee,
unsigned uncurryLevel,
unsigned argIndex,
Expr *arg) {
// We only care about args in the arg list.
if (uncurryLevel != (callee.getDecl()->hasCurriedSelf() ? 1 : 0))
return;
// Get underlying params for both callee and caller, if declared.
auto *calleeParam = getParameterAt(callee.getDecl(), argIndex);
auto *callerParam = dyn_cast_or_null<ParamDecl>(
arg->getReferencedDecl(/*stopAtParenExpr=*/true).getDecl()
);
// (Otherwise, we don't need to do anything.)
if (!calleeParam || !callerParam)
return;
auto calleeDefaultArg = getMagicIdentifierDefaultArgKind(calleeParam);
auto callerDefaultArg = getMagicIdentifierDefaultArgKind(callerParam);
// If one of the parameters doesn't have a default arg, or they're both
// compatible, everything's fine.
if (!calleeDefaultArg || !callerDefaultArg ||
areMagicIdentifiersCompatible(*calleeDefaultArg, *callerDefaultArg))
return;
StringRef calleeDefaultArgString =
MagicIdentifierLiteralExpr::getKindString(*calleeDefaultArg);
StringRef callerDefaultArgString =
MagicIdentifierLiteralExpr::getKindString(*callerDefaultArg);
// Emit main warning
Ctx.Diags.diagnose(arg->getLoc(), diag::default_magic_identifier_mismatch,
callerParam->getName(), callerDefaultArgString,
calleeParam->getName(), calleeDefaultArgString);
// Add "change caller default arg" fixit
SourceLoc callerDefaultArgLoc =
callerParam->getStructuralDefaultExpr()->getLoc();
Ctx.Diags.diagnose(callerDefaultArgLoc,
diag::change_caller_default_to_match_callee,
callerParam->getName(), calleeDefaultArgString)
.fixItReplace(callerDefaultArgLoc, calleeDefaultArgString);
// Add "silence with parens" fixit
Ctx.Diags.diagnose(arg->getLoc(),
diag::silence_default_magic_identifier_mismatch)
.fixItInsert(arg->getStartLoc(), "(")
.fixItInsertAfter(arg->getEndLoc(), ")");
// Point to callee parameter
Ctx.Diags.diagnose(calleeParam, diag::decl_declared_here,
calleeParam->getName());
}
Optional<MagicIdentifierLiteralExpr::Kind>
getMagicIdentifierDefaultArgKind(const ParamDecl *param) {
switch (param->getDefaultArgumentKind()) {
#define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \
case DefaultArgumentKind::NAME: \
return MagicIdentifierLiteralExpr::Kind::NAME;
#include "swift/AST/MagicIdentifierKinds.def"
case DefaultArgumentKind::None:
case DefaultArgumentKind::Normal:
case DefaultArgumentKind::Inherited:
case DefaultArgumentKind::NilLiteral:
case DefaultArgumentKind::EmptyArray:
case DefaultArgumentKind::EmptyDictionary:
case DefaultArgumentKind::StoredProperty:
return None;
}
llvm_unreachable("Unhandled DefaultArgumentKind in "
"getMagicIdentifierDefaultArgKind");
}
static bool
areMagicIdentifiersCompatible(MagicIdentifierLiteralExpr::Kind a,
MagicIdentifierLiteralExpr::Kind b) {
if (a == b)
return true;
// The rest of this handles special compatibility rules between the
// `*SpelledAsFile` cases and various other File-related cases.
//
// The way we're going to do this is a bit magical. We will arrange the
// cases in MagicIdentifierLiteralExpr::Kind so that that they sort in
// this order:
//
// #fileID < Swift 6 #file < #filePath < Swift 5 #file < others
//
// Before we continue, let's verify that this holds.
using Kind = MagicIdentifierLiteralExpr::Kind;
static_assert(Kind::FileID < Kind::FileIDSpelledAsFile,
"#fileID < Swift 6 #file");
static_assert(Kind::FileIDSpelledAsFile < Kind::FilePath,
"Swift 6 #file < #filePath");
static_assert(Kind::FilePath < Kind::FilePathSpelledAsFile,
"#filePath < Swift 5 #file");
static_assert(Kind::FilePathSpelledAsFile < Kind::Line,
"Swift 5 #file < #line");
static_assert(Kind::FilePathSpelledAsFile < Kind::Column,
"Swift 5 #file < #column");
static_assert(Kind::FilePathSpelledAsFile < Kind::Function,
"Swift 5 #file < #function");
static_assert(Kind::FilePathSpelledAsFile < Kind::DSOHandle,
"Swift 5 #file < #dsohandle");
// The rules are all commutative, so we will take the greater of the two
// kinds.
auto maxKind = std::max(a, b);
// Both Swift 6 #file and Swift 5 #file are greater than all of the cases
// they're compatible with. So if `maxCase` is one of those two, the other
// case must have been compatible with it!
return maxKind == Kind::FileIDSpelledAsFile ||
maxKind == Kind::FilePathSpelledAsFile;
}
void checkUseOfModule(DeclRefExpr *E) {
// Allow module values as a part of:
// - ignored base expressions;
// - expressions that failed to type check.
if (auto *ParentExpr = Parent.getAsExpr()) {
if (isa<DotSyntaxBaseIgnoredExpr>(ParentExpr) ||
isa<UnresolvedDotExpr>(ParentExpr))
return;
}
Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_module_type);
}
// Diagnose metatype values that don't appear as part of a property,
// method, or constructor reference.
void checkUseOfMetaTypeName(Expr *E) {
// If we've already checked this at a higher level, we're done.
if (!AlreadyDiagnosedMetatypes.insert(E).second)
return;
// In Swift < 6 warn about plain type name passed as an
// argument to a subscript, dynamic subscript, or ObjC
// literal since it used to be accepted.
DiagnosticBehavior behavior = DiagnosticBehavior::Error;
if (auto *ParentExpr = Parent.getAsExpr()) {
if (ParentExpr->isValidParentOfTypeExpr(E))
return;
if (!Ctx.LangOpts.isSwiftVersionAtLeast(6)) {
if (isa<SubscriptExpr>(ParentExpr) ||
isa<DynamicSubscriptExpr>(ParentExpr) ||
isa<ObjectLiteralExpr>(ParentExpr)) {
auto *argList = ParentExpr->getArgs();
assert(argList);
if (argList->isUnlabeledUnary())
behavior = DiagnosticBehavior::Warning;
}
}
}
// Is this a protocol metatype?
Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_metatype_type)
.limitBehavior(behavior);
// Add fix-it to insert '()', only if this is a metatype of
// non-existential type and has any initializers.
bool isExistential = false;
if (auto metaTy = E->getType()->getAs<MetatypeType>()) {
auto instanceTy = metaTy->getInstanceType();
isExistential = instanceTy->isExistentialType();
if (!isExistential &&
instanceTy->mayHaveMembers() &&
!TypeChecker::lookupMember(const_cast<DeclContext *>(DC), instanceTy,
DeclNameRef::createConstructor()).empty()) {
Ctx.Diags.diagnose(E->getEndLoc(), diag::add_parens_to_type)
.fixItInsertAfter(E->getEndLoc(), "()");
}
}
// Add fix-it to insert ".self".
auto diag = Ctx.Diags.diagnose(E->getEndLoc(), diag::add_self_to_type);
if (E->canAppendPostfixExpression()) {
diag.fixItInsertAfter(E->getEndLoc(), ".self");
} else {
diag.fixItInsert(E->getStartLoc(), "(");
diag.fixItInsertAfter(E->getEndLoc(), ").self");
}
}
void checkUnqualifiedAccessUse(const DeclRefExpr *DRE) {
const Decl *D = DRE->getDecl();
if (!D->getAttrs().hasAttribute<WarnUnqualifiedAccessAttr>())
return;
if (auto *parentExpr = Parent.getAsExpr()) {
if (auto *ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(parentExpr)){
if (!ignoredBase->isImplicit())
return;
}
if (auto *calledBase = dyn_cast<DotSyntaxCallExpr>(parentExpr)) {
if (!calledBase->isImplicit())
return;
}
}
const auto *VD = cast<ValueDecl>(D);
const TypeDecl *declParent =
VD->getDeclContext()->getSelfNominalTypeDecl();
if (!declParent) {
// If the declaration has been validated but not fully type-checked,
// the attribute might be applied to something invalid.
if (!VD->getDeclContext()->isModuleScopeContext())
return;
declParent = VD->getDeclContext()->getParentModule();
}
Ctx.Diags.diagnose(DRE->getLoc(), diag::warn_unqualified_access,
VD->getBaseIdentifier(),
VD->getDescriptiveKind(),
declParent->getDescriptiveKind(),
declParent->getName());
Ctx.Diags.diagnose(VD, diag::decl_declared_here, VD->getName());
if (VD->getDeclContext()->isTypeContext()) {
Ctx.Diags.diagnose(DRE->getLoc(), diag::fix_unqualified_access_member)
.fixItInsert(DRE->getStartLoc(), "self.");
}
DeclContext *topLevelSubcontext = DC->getModuleScopeContext();
auto descriptor = UnqualifiedLookupDescriptor(
DeclNameRef(VD->getBaseName()), topLevelSubcontext, SourceLoc());
auto lookup = evaluateOrDefault(Ctx.evaluator,
UnqualifiedLookupRequest{descriptor}, {});
// Group results by module. Pick an arbitrary result from each module.
llvm::SmallDenseMap<const ModuleDecl*,const ValueDecl*,4> resultsByModule;
for (auto &result : lookup) {
const ValueDecl *value = result.getValueDecl();
resultsByModule.insert(std::make_pair(value->getModuleContext(),value));
}
// Sort by module name.
using ModuleValuePair = std::pair<const ModuleDecl *, const ValueDecl *>;
SmallVector<ModuleValuePair, 4> sortedResults{
resultsByModule.begin(), resultsByModule.end()
};
llvm::array_pod_sort(sortedResults.begin(), sortedResults.end(),
[](const ModuleValuePair *lhs,
const ModuleValuePair *rhs) {
return lhs->first->getName().compare(rhs->first->getName());
});
auto topLevelDiag = diag::fix_unqualified_access_top_level;
if (sortedResults.size() > 1)
topLevelDiag = diag::fix_unqualified_access_top_level_multi;
for (const ModuleValuePair &pair : sortedResults) {
DescriptiveDeclKind k = pair.second->getDescriptiveKind();
SmallString<32> namePlusDot = pair.first->getName().str();
namePlusDot.push_back('.');
Ctx.Diags.diagnose(DRE->getLoc(), topLevelDiag,
namePlusDot, k, pair.first->getName())
.fixItInsert(DRE->getStartLoc(), namePlusDot);
}
}
void checkForDeclWithSpecialTypeCheckingSemantics(const DeclRefExpr *DRE) {
// Referencing type(of:) and other decls with special type-checking
// behavior as functions is not implemented. Maybe we could wrap up the
// special-case behavior in a closure someday...
if (TypeChecker::getDeclTypeCheckingSemantics(DRE->getDecl())
!= DeclTypeCheckingSemantics::Normal) {
Ctx.Diags.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref,
DRE->getDecl()->getBaseIdentifier());
}
}
enum BitcastableNumberKind {
BNK_None = 0,
BNK_Int8,
BNK_Int16,
BNK_Int32,
BNK_Int64,
BNK_Int,
BNK_UInt8,
BNK_UInt16,
BNK_UInt32,
BNK_UInt64,
BNK_UInt,
BNK_Float,
BNK_Double,
};
BitcastableNumberKind getBitcastableNumberKind(Type t) const {
auto decl = t->getNominalOrBoundGenericNominal();
#define MATCH_DECL(type) \
if (decl == Ctx.get##type##Decl()) \
return BNK_##type;
MATCH_DECL(Int8)
MATCH_DECL(Int16)
MATCH_DECL(Int32)
MATCH_DECL(Int64)
MATCH_DECL(Int)
MATCH_DECL(UInt8)
MATCH_DECL(UInt16)
MATCH_DECL(UInt32)
MATCH_DECL(UInt64)
MATCH_DECL(UInt)
MATCH_DECL(Float)
MATCH_DECL(Double)
#undef MATCH_DECL
return BNK_None;
}
static constexpr unsigned BNKPair(BitcastableNumberKind a,
BitcastableNumberKind b) {
return (a << 8) | b;
}
void checkForSuspiciousBitCasts(DeclRefExpr *DRE,
Expr *Parent = nullptr) {
if (DRE->getDecl() != Ctx.getUnsafeBitCast())
return;
if (DRE->getDeclRef().getSubstitutions().empty())
return;
// Don't check the same use of unsafeBitCast twice.
if (!AlreadyDiagnosedBitCasts.insert(DRE).second)
return;
auto subMap = DRE->getDeclRef().getSubstitutions();
auto fromTy = subMap.getReplacementTypes()[0];
auto toTy = subMap.getReplacementTypes()[1];
// Warn about `unsafeBitCast` formulations that are undefined behavior
// or have better-defined alternative APIs that can be used instead.
// If we have a parent ApplyExpr that calls bitcast, extract the argument
// for fixits.
Expr *subExpr = nullptr;
CharSourceRange removeBeforeRange, removeAfterRange;
if (auto apply = dyn_cast_or_null<ApplyExpr>(Parent)) {
subExpr = apply->getArgs()->getExpr(0);
// Determine the fixit range from the start of the application to
// the first argument, `unsafeBitCast(`
removeBeforeRange = CharSourceRange(Ctx.SourceMgr, DRE->getLoc(),
subExpr->getStartLoc());
// Determine the fixit range from the end of the first argument to
// the end of the application, `, to: T.self)`
removeAfterRange = CharSourceRange(Ctx.SourceMgr,
Lexer::getLocForEndOfToken(Ctx.SourceMgr,
subExpr->getEndLoc()),
Lexer::getLocForEndOfToken(Ctx.SourceMgr,
apply->getEndLoc()));
}
// Casting to the same type or a superclass is a no-op.
if (toTy->isEqual(fromTy) ||
toTy->isExactSuperclassOf(fromTy)) {
auto d = Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_is_no_op,
fromTy, toTy);
if (subExpr) {
d.fixItRemoveChars(removeBeforeRange.getStart(),
removeBeforeRange.getEnd())
.fixItRemoveChars(removeAfterRange.getStart(),
removeAfterRange.getEnd());
}
return;
}
if (auto fromFnTy = fromTy->getAs<FunctionType>()) {
if (auto toFnTy = toTy->getAs<FunctionType>()) {
// Casting a nonescaping function to escaping is UB.
// `withoutActuallyEscaping` ought to be used instead.
if (fromFnTy->isNoEscape() && !toFnTy->isNoEscape()) {
Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_away_noescape,
fromTy, toTy);
}
// Changing function representation (say, to try to force a
// @convention(c) function pointer to exist) is also unlikely to work.
if (fromFnTy->getRepresentation() != toFnTy->getRepresentation()) {
Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcasting_to_change_function_rep, fromTy,
toTy);
}
return;
}
}
// Unchecked casting to a subclass is better done by unsafeDowncast.
if (fromTy->isBindableToSuperclassOf(toTy)) {
Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_downcast,
fromTy, toTy)
.fixItReplace(DRE->getNameLoc().getBaseNameLoc(),
"unsafeDowncast");
return;
}
// Casting among pointer types should use the Unsafe*Pointer APIs for
// rebinding typed memory or accessing raw memory instead.
PointerTypeKind fromPTK, toPTK;
Type fromPointee = fromTy->getAnyPointerElementType(fromPTK);
Type toPointee = toTy->getAnyPointerElementType(toPTK);
if (fromPointee && toPointee) {
// Casting to a pointer to the same type or UnsafeRawPointer can use
// normal initializers on the destination type.
if (toPointee->isEqual(fromPointee)
|| isRawPointerKind(toPTK)) {
auto d = Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcasting_to_change_pointer_kind,
fromTy, toTy,
toTy->getStructOrBoundGenericStruct()->getName());
if (subExpr) {
StringRef before, after;
switch (toPTK) {
case PTK_UnsafePointer:
// UnsafePointer(mutablePointer)
before = "UnsafePointer(";
after = ")";
break;
case PTK_UnsafeMutablePointer:
case PTK_AutoreleasingUnsafeMutablePointer:
before = "UnsafeMutablePointer(mutating: ";
after = ")";
break;
case PTK_UnsafeRawPointer:
// UnsafeRawPointer(pointer)
before = "UnsafeRawPointer(";
after = ")";
break;
case PTK_UnsafeMutableRawPointer:
// UnsafeMutableRawPointer(mutating: rawPointer)
before = fromPTK == PTK_UnsafeMutablePointer
? "UnsafeMutableRawPointer("
: "UnsafeMutableRawPointer(mutating: ";
after = ")";
break;
}
d.fixItReplaceChars(removeBeforeRange.getStart(),
removeBeforeRange.getEnd(),
before)
.fixItReplaceChars(removeAfterRange.getStart(),
removeAfterRange.getEnd(),
after);
}
return;
}
// Casting to a different typed pointer type should use
// withMemoryRebound.
if (!isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)) {
Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcasting_to_change_pointee_type,
fromTy, toTy);
return;
}
// Casting a raw pointer to a typed pointer should bind the memory
// (or assume it's already bound).
assert(isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)
&& "unhandled cast combo?!");
Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcasting_to_give_type_to_raw_pointer,
fromTy, toTy);
if (subExpr) {
SmallString<64> fixitBuf;
{
llvm::raw_svector_ostream os(fixitBuf);
os << ".assumingMemoryBound(to: ";
toPointee->print(os);
os << ".self)";
}
Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcast_assume_memory_rebound,
toPointee)
.fixItRemoveChars(removeBeforeRange.getStart(),
removeBeforeRange.getEnd())
.fixItReplaceChars(removeAfterRange.getStart(),
removeAfterRange.getEnd(),
fixitBuf);
fixitBuf.clear();
{
llvm::raw_svector_ostream os(fixitBuf);
os << ".bindMemory(to: ";
toPointee->print(os);
os << ".self, capacity: <""#capacity#"">)";
}
Ctx.Diags.diagnose(DRE->getLoc(),
diag::bitcast_bind_memory,
toPointee)
.fixItRemoveChars(removeBeforeRange.getStart(),
removeBeforeRange.getEnd())
.fixItReplaceChars(removeAfterRange.getStart(),
removeAfterRange.getEnd(),
fixitBuf);
}
return;
}
StringRef replaceBefore, replaceAfter;
Optional<Diag<Type, Type>> diagID;
SmallString<64> replaceBeforeBuf;
// Bitcasting among numeric types should use `bitPattern:` initializers.
auto fromBNK = getBitcastableNumberKind(fromTy);
auto toBNK = getBitcastableNumberKind(toTy);
if (fromBNK && toBNK) {
switch (BNKPair(fromBNK, toBNK)) {
// Combos that can be bitPattern-ed with a constructor
case BNKPair(BNK_Int8, BNK_UInt8):
case BNKPair(BNK_UInt8, BNK_Int8):
case BNKPair(BNK_Int16, BNK_UInt16):
case BNKPair(BNK_UInt16, BNK_Int16):
case BNKPair(BNK_Int32, BNK_UInt32):
case BNKPair(BNK_UInt32, BNK_Int32):
case BNKPair(BNK_Int64, BNK_UInt64):
case BNKPair(BNK_UInt64, BNK_Int64):
case BNKPair(BNK_Int, BNK_UInt):
case BNKPair(BNK_UInt, BNK_Int):
case BNKPair(BNK_UInt32, BNK_Float):
case BNKPair(BNK_UInt64, BNK_Double):
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")";
break;
// Combos that can be bitPattern-ed with a constructor and sign flip
case BNKPair(BNK_Int32, BNK_Float):
case BNKPair(BNK_Int64, BNK_Double):
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
if (fromBNK == BNK_Int32)
os << "UInt32(bitPattern: ";
else
os << "UInt64(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = "))";
break;
// Combos that can be bitPattern-ed with a property
case BNKPair(BNK_Float, BNK_UInt32):
case BNKPair(BNK_Double, BNK_UInt64):
diagID = diag::bitcasting_for_number_bit_pattern_property;
replaceAfter = ".bitPattern";
break;
// Combos that can be bitPattern-ed with a property and sign flip
case BNKPair(BNK_Float, BNK_Int32):
case BNKPair(BNK_Double, BNK_Int64):
diagID = diag::bitcasting_for_number_bit_pattern_property;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")";
break;
// Combos that can be bitPattern-ed with a constructor once (U)Int is
// converted to a sized type.
case BNKPair(BNK_UInt, BNK_Float):
case BNKPair(BNK_Int, BNK_UInt32):
case BNKPair(BNK_UInt, BNK_Int32):
case BNKPair(BNK_Int, BNK_UInt64):
case BNKPair(BNK_UInt, BNK_Int64):
case BNKPair(BNK_UInt, BNK_Double):
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
if (fromBNK == BNK_Int)
os << "Int";
else
os << "UInt";
if (toBNK == BNK_Float
|| toBNK == BNK_Int32
|| toBNK == BNK_UInt32)
os << "32(";
else
os << "64(";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = "))";
break;
case BNKPair(BNK_Int, BNK_Float):
case BNKPair(BNK_Int, BNK_Double):
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: UInt";
if (toBNK == BNK_Float
|| toBNK == BNK_Int32
|| toBNK == BNK_UInt32)
os << "32(bitPattern: Int32(";
else
os << "64(bitPattern: Int64(";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")))";
break;
// Combos that can be bitPattern-ed then converted from a sized type
// to (U)Int.
case BNKPair(BNK_Int32, BNK_UInt):
case BNKPair(BNK_UInt32, BNK_Int):
case BNKPair(BNK_Int64, BNK_UInt):
case BNKPair(BNK_UInt64, BNK_Int):
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(";
if (toBNK == BNK_UInt)
os << "UInt";
else
os << "Int";
if (fromBNK == BNK_Int32 || fromBNK == BNK_UInt32)
os << "32(bitPattern: ";
else
os << "64(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = "))";
break;
case BNKPair(BNK_Float, BNK_UInt):
case BNKPair(BNK_Double, BNK_UInt):
diagID = diag::bitcasting_for_number_bit_pattern_property;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ".bitPattern)";
break;
case BNKPair(BNK_Float, BNK_Int):
case BNKPair(BNK_Double, BNK_Int):
diagID = diag::bitcasting_for_number_bit_pattern_property;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: UInt(";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ".bitPattern))";
break;
// Combos that should be done with a value-preserving initializer.
case BNKPair(BNK_Int, BNK_Int32):
case BNKPair(BNK_Int, BNK_Int64):
case BNKPair(BNK_UInt, BNK_UInt32):
case BNKPair(BNK_UInt, BNK_UInt64):
case BNKPair(BNK_Int32, BNK_Int):
case BNKPair(BNK_Int64, BNK_Int):
case BNKPair(BNK_UInt32, BNK_UInt):
case BNKPair(BNK_UInt64, BNK_UInt):
diagID = diag::bitcasting_to_change_from_unsized_to_sized_int;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << '(';
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")";
break;
default:
// Leave other combos alone.
break;
}
}
// Casting a pointer to an int or back should also use bitPattern
// initializers.
if (fromPointee && toBNK) {
switch (toBNK) {
case BNK_UInt:
case BNK_Int:
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")";
break;
case BNK_UInt64:
case BNK_UInt32:
case BNK_Int64:
case BNK_Int32:
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << '(';
if (toBNK == BNK_UInt32 || toBNK == BNK_UInt64)
os << "UInt(bitPattern: ";
else
os << "Int(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = "))";
break;
default:
break;
}
}
if (fromBNK && toPointee) {
switch (fromBNK) {
case BNK_UInt:
case BNK_Int:
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = ")";
break;
case BNK_UInt64:
case BNK_UInt32:
case BNK_Int64:
case BNK_Int32:
diagID = diag::bitcasting_for_number_bit_pattern_init;
{
llvm::raw_svector_ostream os(replaceBeforeBuf);
toTy->print(os);
os << "(bitPattern: ";
if (fromBNK == BNK_Int32 || fromBNK == BNK_Int64)
os << "Int(";
else
os << "UInt(";
}
replaceBefore = replaceBeforeBuf;
replaceAfter = "))";
break;
default:
break;
}
}
if (diagID) {
auto d = Ctx.Diags.diagnose(DRE->getLoc(), *diagID, fromTy, toTy);
if (subExpr) {
d.fixItReplaceChars(removeBeforeRange.getStart(),
removeBeforeRange.getEnd(),
replaceBefore);
d.fixItReplaceChars(removeAfterRange.getStart(),
removeAfterRange.getEnd(),
replaceAfter);
}
}
}
/// Return true if this is a 'nil' literal. This looks
/// like this if the type is Optional<T>:
///
/// (dot_syntax_call_expr implicit type='Int?'
/// (declref_expr implicit decl=Optional.none)
/// (type_expr type=Int?))
///
/// Or like this if it is any other ExpressibleByNilLiteral type:
///
/// (nil_literal_expr)
///
bool isTypeCheckedOptionalNil(Expr *E) {
if (dyn_cast<NilLiteralExpr>(E)) return true;
auto CE = dyn_cast<ApplyExpr>(E->getSemanticsProvidingExpr());
if (!CE || !CE->isImplicit())
return false;
// First case -- Optional.none
if (auto DRE = dyn_cast<DeclRefExpr>(CE->getSemanticFn()))
return DRE->getDecl() == Ctx.getOptionalNoneDecl();
return false;
}
/// Warn about surprising implicit optional promotions involving operands to
/// calls. Specifically, we warn about these expressions when the 'x'
/// operand is implicitly promoted to optional:
///
/// x ?? y
/// x == nil // also !=
///
void checkOptionalPromotions(ApplyExpr *call) {
// We only care about binary expressions.
auto *BE = dyn_cast<BinaryExpr>(call);
if (!BE) return;
// Dig out the function we're calling.
auto fnExpr = call->getSemanticFn();
if (auto dotSyntax = dyn_cast<DotSyntaxCallExpr>(fnExpr))
fnExpr = dotSyntax->getSemanticFn();
auto DRE = dyn_cast<DeclRefExpr>(fnExpr);
if (!DRE || !DRE->getDecl()->isOperator())
return;
auto lhs = BE->getLHS();
auto rhs = BE->getRHS();
auto calleeName = DRE->getDecl()->getBaseName();
Expr *subExpr = nullptr;
if (calleeName == "??" &&
(subExpr = isImplicitPromotionToOptional(lhs))) {
Ctx.Diags.diagnose(DRE->getLoc(), diag::use_of_qq_on_non_optional_value,
subExpr->getType())
.highlight(lhs->getSourceRange())
.fixItRemove(SourceRange(DRE->getLoc(), rhs->getEndLoc()));
return;
}
if (calleeName == "==" || calleeName == "!=" ||
calleeName == "===" || calleeName == "!==") {
if (((subExpr = isImplicitPromotionToOptional(lhs)) &&
isTypeCheckedOptionalNil(rhs)) ||
(isTypeCheckedOptionalNil(lhs) &&
(subExpr = isImplicitPromotionToOptional(rhs)))) {
bool isTrue = calleeName == "!=" || calleeName == "!==";
Ctx.Diags.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_nil,
subExpr->getType(), isTrue)
.highlight(lhs->getSourceRange())
.highlight(rhs->getSourceRange());
return;
}
}
}
};
DiagnoseWalker Walker(DC, isExprStmt);
const_cast<Expr *>(E)->walk(Walker);
// Diagnose uses of collection literals with defaulted types at the top
// level.
if (auto collection
= dyn_cast<CollectionExpr>(E->getSemanticsProvidingExpr())) {
if (collection->isTypeDefaulted()) {
Walker.checkTypeDefaultedCollectionExpr(
const_cast<CollectionExpr *>(collection));
}
}
}