Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

M5-3-1: Exclude unknown types #852

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions change_notes/2025-02-06-m5-3-1-exclude-unknown-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `M5-3-1` - `EachOperandOfTheOperatorOfTheLogicalAndOrTheLogicalOperatorsShallHaveTypeBool.ql`:
- Consistently exclude results in unevaluated contexts associated with uninstantiated templates, for example `noexcept` specifiers and `static_assert`s.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ where
) and
t = operand.getType() and
not t.getUnderlyingType().getUnspecifiedType() instanceof BoolType and
// Ignore cases where the type is unknown - this will typically be in unevaluated contexts
// within uninstantiated templates. It's necessary to check for this explicitly because
// not all unevaluated contexts are considered to be `isFromUninstantiatedTemplate(_)`,
// e.g. `noexcept` specifiers
not t instanceof UnknownType and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, and we should do this...probably just about everywhere!

How about:

signature class ExamineType = Type;

module TypeOps<ExamineType T> {
  final class TypeFinal = Type;
  class Subtype extends TypeFinal {
    T superType;
    
    SubType() {
      this instanceof UnknownType
      or
      this = superType
      or
      this.getUnspecifiedType() = superType
    }
  }
}

Additional work would have to be done (not right away) to properly support TypeOps<SpecifiedType>::SubType and TypeOps<TypeDefType>::SubType, and to apply the right subtyping recursively for types like PointerType, FunctionType, and class types with inheritance.

But generally, resolveTypedefs(), unspecifiedType(), stripTopLevelSpecifiers() are clunky operations on types that don't truly reflect our intention when we use them. Typically when we use them, we're asking, "is this compatible with." I know that subtyping per se is not quite the right term in C/C++, its worth thinking about what relationship(s) we typically ask about in queries.

not exists(ReferenceType rt |
rt = t.getUnderlyingType().getUnspecifiedType() and rt.getBaseType() instanceof BoolType
) and
Expand Down
11 changes: 10 additions & 1 deletion cpp/autosar/test/rules/M5-3-1/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,13 @@ template <typename T = int> class A {
void f() {
A<int> a;
a.test1();
}
}

template <typename T> constexpr bool some_variable_template_v = false;
template <> constexpr bool some_variable_template_v<int> = true;

template <typename S>
void template_with_no_except() noexcept(some_variable_template_v<S> &&
true) { // COMPLIANT
}
void test_template() { template_with_no_except<int>(); }
Loading