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

[analyzer] Fix uninitialized base class with initializer list when ctor is not declared in the base class (#70464) #70792

Merged
merged 1 commit into from
Nov 1, 2023

Conversation

Snape3058
Copy link
Member

When ctor is not declared in the base class, initializing the base class with the initializer list will not trigger a proper assignment of the base region, as a CXXConstructExpr doing that is not available in the AST.

This patch checks whether the init expr is an InitListExpr under a base initializer, and adds a binding if so.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html labels Oct 31, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 31, 2023

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ella Ma (Snape3058)

Changes

When ctor is not declared in the base class, initializing the base class with the initializer list will not trigger a proper assignment of the base region, as a CXXConstructExpr doing that is not available in the AST.

This patch checks whether the init expr is an InitListExpr under a base initializer, and adds a binding if so.


Full diff: https://github.com/llvm/llvm-project/pull/70792.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+9)
  • (added) clang/test/Analysis/issue-70464.cpp (+69)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 2e67fb953e45611..78dd1147ce1aa7c 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1222,6 +1222,15 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
       PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
       evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
     }
+  } else if (BMI->isBaseInitializer() && isa<InitListExpr>(Init)) {
+    // When the base class is initialized with an initialization list, there
+    // will not be a CXXConstructExpr to initialize the base region. Hence, we
+    // need to make the bind for it.
+    StoreManager &StoreMgr = State->getStateManager().getStoreManager();
+    SVal BaseLoc = StoreMgr.evalDerivedToBase(
+        thisVal, QualType(BMI->getBaseClass(), 0), BMI->isBaseVirtual());
+    SVal InitVal = State->getSVal(Init, stackFrame);
+    evalBind(Tmp, Init, Pred, BaseLoc, InitVal, true);
   } else {
     assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
     Tmp.insert(Pred);
diff --git a/clang/test/Analysis/issue-70464.cpp b/clang/test/Analysis/issue-70464.cpp
new file mode 100644
index 000000000000000..0acced7ae102cac
--- /dev/null
+++ b/clang/test/Analysis/issue-70464.cpp
@@ -0,0 +1,69 @@
+// Refer issue 70464 for more details.
+//
+// When the base class does not have a declared constructor, the base
+// initializer in the constructor of the derived class should use the given
+// initializer list to finish the initialization of the base class.
+//
+// Also testing base constructor and delegate constructor to make sure this
+// change will not break these two cases when a CXXConstructExpr is available.
+
+// RUN: %clang_analyze_cc1 %s -verify -analyzer-checker=core,debug.ExprInspection
+
+void clang_analyzer_dump(int);
+
+namespace init_list {
+
+struct foo {
+  int foox;
+};
+
+class bar : public foo {
+public:
+  bar() : foo{42} {
+    // The dereference to this->foox below should be initialized properly.
+    clang_analyzer_dump(this->foox); // expected-warning{{42 S32b}}
+  }
+};
+
+void entry() { bar test; }
+
+} // namespace init_list
+
+namespace base_ctor_call {
+
+void clang_analyzer_dump(int);
+
+struct foo {
+  int foox;
+  foo(int x) : foox(x) {}
+};
+
+class bar : public foo {
+public:
+  bar() : foo{42} {
+    clang_analyzer_dump(this->foox); // expected-warning{{42 S32b}}
+  }
+};
+
+void entry() { bar test; }
+
+} // namespace base_ctor_call
+
+namespace delegate_ctor_call {
+
+void clang_analyzer_dump(int);
+
+struct foo {
+  int foox;
+};
+
+struct bar : foo {
+  bar(int parx) { this->foox = parx; }
+  bar() : bar{42} {
+    clang_analyzer_dump(this->foox); // expected-warning{{42 S32b}}
+  }
+};
+
+void entry() { bar test; }
+
+} // namespace delegate_ctor_call

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

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

I must admit that I didn't look at the issue too much, but this patch speaks for itself.
Clean, to the point, and meets our conventions. Kudos.
I only have minor remarks.

And be sure to mention Fixes #70464 in the PR/commit message to auto-close the issue.

clang/test/Analysis/issue-70464.cpp Outdated Show resolved Hide resolved
clang/test/Analysis/issue-70464.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp Outdated Show resolved Hide resolved
clang/test/Analysis/issue-70464.cpp Show resolved Hide resolved
clang/test/Analysis/issue-70464.cpp Outdated Show resolved Hide resolved
clang/test/Analysis/issue-70464.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

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

Looks great!

FYI when submitting patches to GH try not to force-push to help the UI for following lines having comments. Otherwise, they will be marked as "outdated" and become hard to dig up and relate to new line locations. This was easy this time, as the size of the PR was minimal (as it should be).

After the PR is approved and all conversations resolved; the author can either manually force-push to squash the commits or use the "squash-merge" to let GH do it.

…or is not declared in the base class

Fixes llvm#70464

When ctor is not declared in the base class, initializing the base class
with the initializer list will not trigger a proper assignment of the
base region, as a CXXConstructExpr doing that is not available in the
AST.

This patch checks whether the init expr is an InitListExpr under a base
initializer, and adds a binding if so.
@Snape3058 Snape3058 merged commit b6b31e7 into llvm:main Nov 1, 2023
3 checks passed
@steakhal
Copy link
Contributor

steakhal commented Nov 2, 2023

I wanted to highlight that this PR resolved a bunch of open issues, namely: #61919, #59493, #54533
Thank you!

So I think we should mention this in the release notes for clang-18 in some way. I'll keep this in mind.

@Snape3058
Copy link
Member Author

As #59493 is an array, which is different from the test case I provided and the ones in #61919 and #54533,
although this pr can correctly handle the array case, do I still need to add the array one to the test case?

// Additional test case from issue #59493
namespace init_list_array {

struct Base {
  int foox[1];
};

class Derived : public Base {
public:
  Derived() : Base{{42}} {
    // The dereference to this->foox below should be initialized properly.
    clang_analyzer_dump(this->foox[0]); // expected-warning{{42 S32b}}
    clang_analyzer_dump(foox[0]); // expected-warning{{42 S32b}}
  }
};

void entry() { Derived test; }

} // namespace init_list_array 

@steakhal
Copy link
Contributor

steakhal commented Nov 2, 2023

As #59493 is an array, which is different from the test case I provided and the ones in #61919 and #54533, although this pr can correctly handle the array case, do I still need to add the array one to the test case?

It would be really nice if you could. Thanks!

@Snape3058
Copy link
Member Author

Do I need to

  • create a new PR?
  • push directly to this PR on the original branch Snape3058:issue-70464?
  • commit directly without revision?

Which operation is correct?

(Sorry for not familiar with GitHub) -(

@steakhal
Copy link
Contributor

steakhal commented Nov 2, 2023

Do I need to

* create a new PR?

* push directly to this PR on the original branch `Snape3058:issue-70464`?

* commit directly without revision?

Which operation is correct?

(Sorry for not familiar with GitHub) -(

I'd prefer a new PR, and mentioning the original PR (the fix) and the issue for which it adds the test.

@Snape3058
Copy link
Member Author

OK, thanks~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants