| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "nullability/inference/inferable.h" |
| |
| #include "nullability/type_nullability.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclBase.h" |
| #include "clang/AST/DeclTemplate.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Basic/LLVM.h" |
| |
| namespace clang::tidy::nullability { |
| |
| bool hasInferable(QualType T) { |
| return isSupportedPointerType(T.getNonReferenceType()); |
| } |
| |
| int countInferableSlots(const Decl& D) { |
| if (const auto* Func = dyn_cast<FunctionDecl>(&D)) { |
| int Slots = 0; |
| if (hasInferable(Func->getReturnType())) ++Slots; |
| for (auto* P : Func->parameters()) |
| if (hasInferable(P->getType())) ++Slots; |
| return Slots; |
| } |
| if (const auto* Field = dyn_cast<FieldDecl>(&D)) { |
| return hasInferable(Field->getType()) ? 1 : 0; |
| } |
| if (const auto* Var = dyn_cast<VarDecl>(&D)) { |
| return hasInferable(Var->getType()) ? 1 : 0; |
| } |
| return 0; |
| } |
| |
| bool isInferenceTarget(const Decl& D) { |
| if (const auto* Func = dyn_cast<FunctionDecl>(&D)) { |
| return |
| // Function templates are in principle inferable. |
| // However since we don't analyze their bodies, and other |
| // implementations cannot interact with them directly, we can't perform |
| // any nontrivial inference, just propagate annotations across redecls. |
| // For now, we don't do this as some infra (NullabilityWalker) doesn't |
| // work on dependent code. |
| !Func->isDependentContext() && |
| // Inferring properties of template instantiations isn't useful in |
| // itself. We can't record them anywhere unless they apply to the |
| // template in general. |
| // TODO: work out in what circumstances that would be safe. |
| !Func->getTemplateInstantiationPattern() && |
| // builtins can't be annotated and are irregular in their type checking |
| // and in other ways, leading to violations of otherwise sound |
| // assumptions. |
| // If we find that their nullability is unexpectedly leaking into |
| // programs under analysis in significant ways, we can hardcode this |
| // small set of functions. |
| Func->getBuiltinID() == 0 && |
| // Implicit functions cannot be annotated. |
| !Func->isImplicit() && |
| // Do the most expensive check last. |
| countPointersInType(Func->getType()) > 0; |
| } |
| if (const auto* Field = dyn_cast<FieldDecl>(&D)) { |
| return |
| // See comments above regarding dependent contexts and templates. |
| !Field->getDeclContext()->isDependentContext() && |
| !isa<ClassTemplateSpecializationDecl>(Field->getParent()) && |
| // Do the most expensive check last. |
| countPointersInType(Field->getType()) > 0; |
| } |
| if (const auto* Var = dyn_cast<VarDecl>(&D)) { |
| // Include static member variables, global variables, and local variables, |
| // including static variables defined in a function. |
| |
| // Exclude parameters, which are handled as part of their enclosing function |
| // declaration. |
| if (isa<ParmVarDecl>(Var)) return false; |
| |
| // Exclude variables in templates and dependent contexts as well as variable |
| // templates. See comments above regarding similar restrictions on |
| // functions. |
| const DeclContext* DC = Var->getDeclContext(); |
| if (DC->isDependentContext()) return false; |
| if (auto* EnclosingFunc = dyn_cast<FunctionDecl>(DC); |
| EnclosingFunc && EnclosingFunc->isTemplateInstantiation()) |
| return false; |
| return !Var->isTemplated() && !Var->getTemplateInstantiationPattern() && |
| // Do the most expensive check last. |
| countPointersInType(Var->getType()) > 0; |
| } |
| return false; |
| } |
| |
| } // namespace clang::tidy::nullability |