Skip to content

Commit 81fa098

Browse files
authored
Merge pull request swiftlang#8184 from haoNoQ/static-analyzer-cherrypicks-2024-02
🍒 Static Analyzer cherrypicks
2 parents 838142a + f786d8a commit 81fa098

35 files changed

+601
-82
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,13 @@ def Suppress : DeclOrStmtAttr {
27492749
let Spellings = [CXX11<"gsl", "suppress">, Clang<"suppress">];
27502750
let Args = [VariadicStringArgument<"DiagnosticIdentifiers">];
27512751
let Accessors = [Accessor<"isGSL", [CXX11<"gsl", "suppress">]>];
2752+
// There's no fundamental reason why we can't simply accept all Decls
2753+
// but let's make a short list so that to avoid supporting something weird
2754+
// by accident. We can always expand the list later.
2755+
let Subjects = SubjectList<[
2756+
Stmt, Var, Field, ObjCProperty, Function, ObjCMethod, Record, ObjCInterface,
2757+
ObjCImplementation, Namespace, Empty
2758+
], ErrorDiag, "variables, functions, structs, interfaces, and namespaces">;
27522759
let Documentation = [SuppressDocs];
27532760
}
27542761

clang/include/clang/Basic/AttrDocs.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5344,6 +5344,29 @@ Putting the attribute on a compound statement suppresses all warnings in scope:
53445344
}
53455345
}
53465346

5347+
The attribute can also be placed on entire declarations of functions, classes,
5348+
variables, member variables, and so on, to suppress warnings related
5349+
to the declarations themselves. When used this way, the attribute additionally
5350+
suppresses all warnings in the lexical scope of the declaration:
5351+
5352+
.. code-block:: c++
5353+
5354+
class [[clang::suppress]] C {
5355+
int foo() {
5356+
int *x = nullptr;
5357+
...
5358+
return *x; // warnings suppressed in the entire class scope
5359+
}
5360+
5361+
int bar();
5362+
};
5363+
5364+
int C::bar() {
5365+
int *x = nullptr;
5366+
...
5367+
return *x; // warning NOT suppressed! - not lexically nested in 'class C{}'
5368+
}
5369+
53475370
Some static analysis warnings are accompanied by one or more notes, and the
53485371
line of code against which the warning is emitted isn't necessarily the best
53495372
for suppression purposes. In such cases the tools are allowed to implement

clang/include/clang/Rewrite/Core/HTMLRewrite.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ class RewriteBuffer;
2424
class Preprocessor;
2525

2626
namespace html {
27+
struct RelexRewriteCache;
28+
using RelexRewriteCacheRef = std::shared_ptr<RelexRewriteCache>;
29+
30+
/// If you need to rewrite the same file multiple times, you can instantiate
31+
/// a RelexRewriteCache and refer functions such as SyntaxHighlight()
32+
/// and HighlightMacros() to it so that to avoid re-lexing the file each time.
33+
/// The cache may outlive the rewriter as long as cached FileIDs and source
34+
/// locations continue to make sense for the translation unit as a whole.
35+
RelexRewriteCacheRef instantiateRelexRewriteCache();
2736

2837
/// HighlightRange - Highlight a range in the source code with the specified
2938
/// start/end tags. B/E must be in the same file. This ensures that
@@ -67,13 +76,15 @@ namespace html {
6776

6877
/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with
6978
/// information about keywords, comments, etc.
70-
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP);
79+
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP,
80+
RelexRewriteCacheRef Cache = nullptr);
7181

7282
/// HighlightMacros - This uses the macro table state from the end of the
7383
/// file, to reexpand macros and insert (into the HTML) information about the
7484
/// macro expansions. This won't be perfectly perfect, but it will be
7585
/// reasonably close.
76-
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP);
86+
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP,
87+
RelexRewriteCacheRef Cache = nullptr);
7788

7889
} // end html namespace
7990
} // end clang namespace

clang/include/clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/SmallVector.h"
2020

2121
namespace clang {
22+
class ASTContext;
2223
class Decl;
2324

2425
namespace ento {
@@ -27,6 +28,8 @@ class PathDiagnosticLocation;
2728

2829
class BugSuppression {
2930
public:
31+
explicit BugSuppression(const ASTContext &ACtx) : ACtx(ACtx) {}
32+
3033
using DiagnosticIdentifierList = llvm::ArrayRef<llvm::StringRef>;
3134

3235
/// Return true if the given bug report was explicitly suppressed by the user.
@@ -45,6 +48,8 @@ class BugSuppression {
4548
llvm::SmallVector<SourceRange, EXPECTED_NUMBER_OF_SUPPRESSIONS>;
4649

4750
llvm::DenseMap<const Decl *, CachedRanges> CachedSuppressionLocations;
51+
52+
const ASTContext &ACtx;
4853
};
4954

5055
} // end namespace ento

clang/lib/Rewrite/HTMLRewrite.cpp

Lines changed: 116 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
#include "llvm/Support/MemoryBuffer.h"
2222
#include "llvm/Support/raw_ostream.h"
2323
#include <memory>
24-
using namespace clang;
2524

25+
using namespace clang;
26+
using namespace llvm;
27+
using namespace html;
2628

2729
/// HighlightRange - Highlight a range in the source code with the specified
2830
/// start/end tags. B/E must be in the same file. This ensures that
@@ -104,6 +106,32 @@ void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
104106
}
105107
}
106108

109+
namespace clang::html {
110+
struct RelexRewriteCache {
111+
// These structs mimic input arguments of HighlightRange().
112+
struct Highlight {
113+
SourceLocation B, E;
114+
std::string StartTag, EndTag;
115+
bool IsTokenRange;
116+
};
117+
struct RawHighlight {
118+
unsigned B, E;
119+
std::string StartTag, EndTag;
120+
};
121+
122+
// SmallVector isn't appropriate because these vectors are almost never small.
123+
using HighlightList = std::vector<Highlight>;
124+
using RawHighlightList = std::vector<RawHighlight>;
125+
126+
DenseMap<FileID, RawHighlightList> SyntaxHighlights;
127+
DenseMap<FileID, HighlightList> MacroHighlights;
128+
};
129+
} // namespace clang::html
130+
131+
html::RelexRewriteCacheRef html::instantiateRelexRewriteCache() {
132+
return std::make_shared<RelexRewriteCache>();
133+
}
134+
107135
void html::EscapeText(Rewriter &R, FileID FID,
108136
bool EscapeSpaces, bool ReplaceTabs) {
109137

@@ -442,13 +470,18 @@ input.spoilerhider:checked + label + .spoiler{
442470
/// information about keywords, macro expansions etc. This uses the macro
443471
/// table state from the end of the file, so it won't be perfectly perfect,
444472
/// but it will be reasonably close.
445-
void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
446-
RewriteBuffer &RB = R.getEditBuffer(FID);
473+
static void SyntaxHighlightImpl(
474+
Rewriter &R, FileID FID, const Preprocessor &PP,
475+
llvm::function_ref<void(RewriteBuffer &, unsigned, unsigned, const char *,
476+
const char *, const char *)>
477+
HighlightRangeCallback) {
447478

479+
RewriteBuffer &RB = R.getEditBuffer(FID);
448480
const SourceManager &SM = PP.getSourceManager();
449481
llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
482+
const char *BufferStart = FromFile.getBuffer().data();
483+
450484
Lexer L(FID, FromFile, SM, PP.getLangOpts());
451-
const char *BufferStart = L.getBuffer().data();
452485

453486
// Inform the preprocessor that we want to retain comments as tokens, so we
454487
// can highlight them.
@@ -475,13 +508,13 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
475508

476509
// If this is a pp-identifier, for a keyword, highlight it as such.
477510
if (Tok.isNot(tok::identifier))
478-
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
479-
"<span class='keyword'>", "</span>");
511+
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
512+
"<span class='keyword'>", "</span>");
480513
break;
481514
}
482515
case tok::comment:
483-
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
484-
"<span class='comment'>", "</span>");
516+
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
517+
"<span class='comment'>", "</span>");
485518
break;
486519
case tok::utf8_string_literal:
487520
// Chop off the u part of u8 prefix
@@ -498,8 +531,8 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
498531
[[fallthrough]];
499532
case tok::string_literal:
500533
// FIXME: Exclude the optional ud-suffix from the highlighted range.
501-
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
502-
"<span class='string_literal'>", "</span>");
534+
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
535+
"<span class='string_literal'>", "</span>");
503536
break;
504537
case tok::hash: {
505538
// If this is a preprocessor directive, all tokens to end of line are too.
@@ -516,8 +549,8 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
516549
}
517550

518551
// Find end of line. This is a hack.
519-
HighlightRange(RB, TokOffs, TokEnd, BufferStart,
520-
"<span class='directive'>", "</span>");
552+
HighlightRangeCallback(RB, TokOffs, TokEnd, BufferStart,
553+
"<span class='directive'>", "</span>");
521554

522555
// Don't skip the next token.
523556
continue;
@@ -527,12 +560,43 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
527560
L.LexFromRawLexer(Tok);
528561
}
529562
}
563+
void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP,
564+
RelexRewriteCacheRef Cache) {
565+
RewriteBuffer &RB = R.getEditBuffer(FID);
566+
const SourceManager &SM = PP.getSourceManager();
567+
llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
568+
const char *BufferStart = FromFile.getBuffer().data();
569+
570+
if (Cache) {
571+
auto CacheIt = Cache->SyntaxHighlights.find(FID);
572+
if (CacheIt != Cache->SyntaxHighlights.end()) {
573+
for (const RelexRewriteCache::RawHighlight &H : CacheIt->second) {
574+
HighlightRange(RB, H.B, H.E, BufferStart, H.StartTag.data(),
575+
H.EndTag.data());
576+
}
577+
return;
578+
}
579+
}
580+
581+
// "Every time you would call HighlightRange, cache the inputs as well."
582+
auto HighlightRangeCallback = [&](RewriteBuffer &RB, unsigned B, unsigned E,
583+
const char *BufferStart,
584+
const char *StartTag, const char *EndTag) {
585+
HighlightRange(RB, B, E, BufferStart, StartTag, EndTag);
586+
587+
if (Cache)
588+
Cache->SyntaxHighlights[FID].push_back({B, E, StartTag, EndTag});
589+
};
590+
591+
SyntaxHighlightImpl(R, FID, PP, HighlightRangeCallback);
592+
}
593+
594+
static void HighlightMacrosImpl(
595+
Rewriter &R, FileID FID, const Preprocessor &PP,
596+
llvm::function_ref<void(Rewriter &, SourceLocation, SourceLocation,
597+
const char *, const char *, bool)>
598+
HighlightRangeCallback) {
530599

531-
/// HighlightMacros - This uses the macro table state from the end of the
532-
/// file, to re-expand macros and insert (into the HTML) information about the
533-
/// macro expansions. This won't be perfectly perfect, but it will be
534-
/// reasonably close.
535-
void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
536600
// Re-lex the raw token stream into a token buffer.
537601
const SourceManager &SM = PP.getSourceManager();
538602
std::vector<Token> TokenStream;
@@ -659,11 +723,44 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
659723
// get highlighted.
660724
Expansion = "<span class='macro_popup'>" + Expansion + "</span></span>";
661725

662-
HighlightRange(R, LLoc.getBegin(), LLoc.getEnd(), "<span class='macro'>",
663-
Expansion.c_str(), LLoc.isTokenRange());
726+
HighlightRangeCallback(R, LLoc.getBegin(), LLoc.getEnd(),
727+
"<span class='macro'>", Expansion.c_str(),
728+
LLoc.isTokenRange());
664729
}
665730

666731
// Restore the preprocessor's old state.
667732
TmpPP.setDiagnostics(*OldDiags);
668733
TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
669734
}
735+
736+
/// HighlightMacros - This uses the macro table state from the end of the
737+
/// file, to re-expand macros and insert (into the HTML) information about the
738+
/// macro expansions. This won't be perfectly perfect, but it will be
739+
/// reasonably close.
740+
void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP,
741+
RelexRewriteCacheRef Cache) {
742+
if (Cache) {
743+
auto CacheIt = Cache->MacroHighlights.find(FID);
744+
if (CacheIt != Cache->MacroHighlights.end()) {
745+
for (const RelexRewriteCache::Highlight &H : CacheIt->second) {
746+
HighlightRange(R, H.B, H.E, H.StartTag.data(), H.EndTag.data(),
747+
H.IsTokenRange);
748+
}
749+
return;
750+
}
751+
}
752+
753+
// "Every time you would call HighlightRange, cache the inputs as well."
754+
auto HighlightRangeCallback = [&](Rewriter &R, SourceLocation B,
755+
SourceLocation E, const char *StartTag,
756+
const char *EndTag, bool isTokenRange) {
757+
HighlightRange(R, B, E, StartTag, EndTag, isTokenRange);
758+
759+
if (Cache) {
760+
Cache->MacroHighlights[FID].push_back(
761+
{B, E, StartTag, EndTag, isTokenRange});
762+
}
763+
};
764+
765+
HighlightMacrosImpl(R, FID, PP, HighlightRangeCallback);
766+
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,9 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
29722972
S.mergeHLSLNumThreadsAttr(D, *NT, NT->getX(), NT->getY(), NT->getZ());
29732973
else if (const auto *SA = dyn_cast<HLSLShaderAttr>(Attr))
29742974
NewAttr = S.mergeHLSLShaderAttr(D, *SA, SA->getType());
2975+
else if (const auto *SupA = dyn_cast<SuppressAttr>(Attr))
2976+
// Do nothing. Each redeclaration should be suppressed separately.
2977+
NewAttr = nullptr;
29752978
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
29762979
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
29772980

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5213,11 +5213,6 @@ static void handleSuppressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
52135213
// Suppression attribute with GSL spelling requires at least 1 argument.
52145214
if (!AL.checkAtLeastNumArgs(S, 1))
52155215
return;
5216-
} else if (!isa<VarDecl>(D)) {
5217-
// Analyzer suppression applies only to variables and statements.
5218-
S.Diag(AL.getLoc(), diag::err_attribute_wrong_decl_type_str)
5219-
<< AL << 0 << "variables and statements";
5220-
return;
52215216
}
52225217

52235218
std::vector<StringRef> DiagnosticIdentifiers;

clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
161161

162162
PathDiagnosticLocation L =
163163
PathDiagnosticLocation::create(Ivar, BR.getSourceManager());
164-
BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
165-
os.str(), L);
164+
BR.EmitBasicReport(ID, Checker, "Unused instance variable",
165+
"Optimization", os.str(), L);
166166
}
167167
}
168168

clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,16 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
3434
}
3535
if (auto *call = dyn_cast<CallExpr>(E)) {
3636
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
37-
std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
38-
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
39-
E = memberCall->getImplicitObjectArgument();
40-
if (StopAtFirstRefCountedObj) {
41-
return {E, true};
37+
if (auto *decl = memberCall->getMethodDecl()) {
38+
std::optional<bool> IsGetterOfRefCt =
39+
isGetterOfRefCounted(memberCall->getMethodDecl());
40+
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
41+
E = memberCall->getImplicitObjectArgument();
42+
if (StopAtFirstRefCountedObj) {
43+
return {E, true};
44+
}
45+
continue;
4246
}
43-
continue;
4447
}
4548
}
4649

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R)
8484
if (AnyInconclusiveBase)
8585
return std::nullopt;
8686

87+
Paths.clear();
8788
const auto hasPublicDerefInBase =
8889
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
8990
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
@@ -154,6 +155,7 @@ std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
154155

155156
if (((className == "Ref" || className == "RefPtr") &&
156157
methodName == "get") ||
158+
(className == "Ref" && methodName == "ptr") ||
157159
((className == "String" || className == "AtomString" ||
158160
className == "AtomStringImpl" || className == "UniqueString" ||
159161
className == "UniqueStringImpl" || className == "Identifier") &&

0 commit comments

Comments
 (0)