xref: /freebsd/contrib/llvm-project/clang/lib/Basic/Warnings.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===--- Warnings.cpp - C-Language Front-end ------------------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // Command line warning options handler.
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12*0b57cec5SDimitry Andric //
13*0b57cec5SDimitry Andric // This file is responsible for handling all warning options. This includes
14*0b57cec5SDimitry Andric // a number of -Wfoo options and their variants, which are driven by TableGen-
15*0b57cec5SDimitry Andric // generated data, and the special cases -pedantic, -pedantic-errors, -w,
16*0b57cec5SDimitry Andric // -Werror and -Wfatal-errors.
17*0b57cec5SDimitry Andric //
18*0b57cec5SDimitry Andric // Each warning option controls any number of actual warnings.
19*0b57cec5SDimitry Andric // Given a warning option 'foo', the following are valid:
20*0b57cec5SDimitry Andric //    -Wfoo, -Wno-foo, -Werror=foo, -Wfatal-errors=foo
21*0b57cec5SDimitry Andric //
22*0b57cec5SDimitry Andric // Remark options are also handled here, analogously, except that they are much
23*0b57cec5SDimitry Andric // simpler because a remark can't be promoted to an error.
24*0b57cec5SDimitry Andric #include "clang/Basic/AllDiagnostics.h"
25*0b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h"
26*0b57cec5SDimitry Andric #include "clang/Basic/DiagnosticOptions.h"
27*0b57cec5SDimitry Andric #include <algorithm>
28*0b57cec5SDimitry Andric #include <cstring>
29*0b57cec5SDimitry Andric #include <utility>
30*0b57cec5SDimitry Andric using namespace clang;
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric // EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning
33*0b57cec5SDimitry Andric // opts
34*0b57cec5SDimitry Andric static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags,
35*0b57cec5SDimitry Andric                                    diag::Flavor Flavor, StringRef Prefix,
36*0b57cec5SDimitry Andric                                    StringRef Opt) {
37*0b57cec5SDimitry Andric   StringRef Suggestion = DiagnosticIDs::getNearestOption(Flavor, Opt);
38*0b57cec5SDimitry Andric   Diags.Report(diag::warn_unknown_diag_option)
39*0b57cec5SDimitry Andric     << (Flavor == diag::Flavor::WarningOrError ? 0 : 1) << (Prefix.str() += Opt)
40*0b57cec5SDimitry Andric     << !Suggestion.empty() << (Prefix.str() += Suggestion);
41*0b57cec5SDimitry Andric }
42*0b57cec5SDimitry Andric 
43*0b57cec5SDimitry Andric void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
44*0b57cec5SDimitry Andric                                   const DiagnosticOptions &Opts,
45*0b57cec5SDimitry Andric                                   bool ReportDiags) {
46*0b57cec5SDimitry Andric   Diags.setSuppressSystemWarnings(true);  // Default to -Wno-system-headers
47*0b57cec5SDimitry Andric   Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings);
48*0b57cec5SDimitry Andric   Diags.setShowOverloads(Opts.getShowOverloads());
49*0b57cec5SDimitry Andric 
50*0b57cec5SDimitry Andric   Diags.setElideType(Opts.ElideType);
51*0b57cec5SDimitry Andric   Diags.setPrintTemplateTree(Opts.ShowTemplateTree);
52*0b57cec5SDimitry Andric   Diags.setShowColors(Opts.ShowColors);
53*0b57cec5SDimitry Andric 
54*0b57cec5SDimitry Andric   // Handle -ferror-limit
55*0b57cec5SDimitry Andric   if (Opts.ErrorLimit)
56*0b57cec5SDimitry Andric     Diags.setErrorLimit(Opts.ErrorLimit);
57*0b57cec5SDimitry Andric   if (Opts.TemplateBacktraceLimit)
58*0b57cec5SDimitry Andric     Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit);
59*0b57cec5SDimitry Andric   if (Opts.ConstexprBacktraceLimit)
60*0b57cec5SDimitry Andric     Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit);
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric   // If -pedantic or -pedantic-errors was specified, then we want to map all
63*0b57cec5SDimitry Andric   // extension diagnostics onto WARNING or ERROR unless the user has futz'd
64*0b57cec5SDimitry Andric   // around with them explicitly.
65*0b57cec5SDimitry Andric   if (Opts.PedanticErrors)
66*0b57cec5SDimitry Andric     Diags.setExtensionHandlingBehavior(diag::Severity::Error);
67*0b57cec5SDimitry Andric   else if (Opts.Pedantic)
68*0b57cec5SDimitry Andric     Diags.setExtensionHandlingBehavior(diag::Severity::Warning);
69*0b57cec5SDimitry Andric   else
70*0b57cec5SDimitry Andric     Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
71*0b57cec5SDimitry Andric 
72*0b57cec5SDimitry Andric   SmallVector<diag::kind, 10> _Diags;
73*0b57cec5SDimitry Andric   const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs =
74*0b57cec5SDimitry Andric     Diags.getDiagnosticIDs();
75*0b57cec5SDimitry Andric   // We parse the warning options twice.  The first pass sets diagnostic state,
76*0b57cec5SDimitry Andric   // while the second pass reports warnings/errors.  This has the effect that
77*0b57cec5SDimitry Andric   // we follow the more canonical "last option wins" paradigm when there are
78*0b57cec5SDimitry Andric   // conflicting options.
79*0b57cec5SDimitry Andric   for (unsigned Report = 0, ReportEnd = 2; Report != ReportEnd; ++Report) {
80*0b57cec5SDimitry Andric     bool SetDiagnostic = (Report == 0);
81*0b57cec5SDimitry Andric 
82*0b57cec5SDimitry Andric     // If we've set the diagnostic state and are not reporting diagnostics then
83*0b57cec5SDimitry Andric     // we're done.
84*0b57cec5SDimitry Andric     if (!SetDiagnostic && !ReportDiags)
85*0b57cec5SDimitry Andric       break;
86*0b57cec5SDimitry Andric 
87*0b57cec5SDimitry Andric     for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) {
88*0b57cec5SDimitry Andric       const auto Flavor = diag::Flavor::WarningOrError;
89*0b57cec5SDimitry Andric       StringRef Opt = Opts.Warnings[i];
90*0b57cec5SDimitry Andric       StringRef OrigOpt = Opts.Warnings[i];
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric       // Treat -Wformat=0 as an alias for -Wno-format.
93*0b57cec5SDimitry Andric       if (Opt == "format=0")
94*0b57cec5SDimitry Andric         Opt = "no-format";
95*0b57cec5SDimitry Andric 
96*0b57cec5SDimitry Andric       // Check to see if this warning starts with "no-", if so, this is a
97*0b57cec5SDimitry Andric       // negative form of the option.
98*0b57cec5SDimitry Andric       bool isPositive = true;
99*0b57cec5SDimitry Andric       if (Opt.startswith("no-")) {
100*0b57cec5SDimitry Andric         isPositive = false;
101*0b57cec5SDimitry Andric         Opt = Opt.substr(3);
102*0b57cec5SDimitry Andric       }
103*0b57cec5SDimitry Andric 
104*0b57cec5SDimitry Andric       // Figure out how this option affects the warning.  If -Wfoo, map the
105*0b57cec5SDimitry Andric       // diagnostic to a warning, if -Wno-foo, map it to ignore.
106*0b57cec5SDimitry Andric       diag::Severity Mapping =
107*0b57cec5SDimitry Andric           isPositive ? diag::Severity::Warning : diag::Severity::Ignored;
108*0b57cec5SDimitry Andric 
109*0b57cec5SDimitry Andric       // -Wsystem-headers is a special case, not driven by the option table.  It
110*0b57cec5SDimitry Andric       // cannot be controlled with -Werror.
111*0b57cec5SDimitry Andric       if (Opt == "system-headers") {
112*0b57cec5SDimitry Andric         if (SetDiagnostic)
113*0b57cec5SDimitry Andric           Diags.setSuppressSystemWarnings(!isPositive);
114*0b57cec5SDimitry Andric         continue;
115*0b57cec5SDimitry Andric       }
116*0b57cec5SDimitry Andric 
117*0b57cec5SDimitry Andric       // -Weverything is a special case as well.  It implicitly enables all
118*0b57cec5SDimitry Andric       // warnings, including ones not explicitly in a warning group.
119*0b57cec5SDimitry Andric       if (Opt == "everything") {
120*0b57cec5SDimitry Andric         if (SetDiagnostic) {
121*0b57cec5SDimitry Andric           if (isPositive) {
122*0b57cec5SDimitry Andric             Diags.setEnableAllWarnings(true);
123*0b57cec5SDimitry Andric           } else {
124*0b57cec5SDimitry Andric             Diags.setEnableAllWarnings(false);
125*0b57cec5SDimitry Andric             Diags.setSeverityForAll(Flavor, diag::Severity::Ignored);
126*0b57cec5SDimitry Andric           }
127*0b57cec5SDimitry Andric         }
128*0b57cec5SDimitry Andric         continue;
129*0b57cec5SDimitry Andric       }
130*0b57cec5SDimitry Andric 
131*0b57cec5SDimitry Andric       // -Werror/-Wno-error is a special case, not controlled by the option
132*0b57cec5SDimitry Andric       // table. It also has the "specifier" form of -Werror=foo and -Werror-foo.
133*0b57cec5SDimitry Andric       if (Opt.startswith("error")) {
134*0b57cec5SDimitry Andric         StringRef Specifier;
135*0b57cec5SDimitry Andric         if (Opt.size() > 5) {  // Specifier must be present.
136*0b57cec5SDimitry Andric           if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) {
137*0b57cec5SDimitry Andric             if (Report)
138*0b57cec5SDimitry Andric               Diags.Report(diag::warn_unknown_warning_specifier)
139*0b57cec5SDimitry Andric                 << "-Werror" << ("-W" + OrigOpt.str());
140*0b57cec5SDimitry Andric             continue;
141*0b57cec5SDimitry Andric           }
142*0b57cec5SDimitry Andric           Specifier = Opt.substr(6);
143*0b57cec5SDimitry Andric         }
144*0b57cec5SDimitry Andric 
145*0b57cec5SDimitry Andric         if (Specifier.empty()) {
146*0b57cec5SDimitry Andric           if (SetDiagnostic)
147*0b57cec5SDimitry Andric             Diags.setWarningsAsErrors(isPositive);
148*0b57cec5SDimitry Andric           continue;
149*0b57cec5SDimitry Andric         }
150*0b57cec5SDimitry Andric 
151*0b57cec5SDimitry Andric         if (SetDiagnostic) {
152*0b57cec5SDimitry Andric           // Set the warning as error flag for this specifier.
153*0b57cec5SDimitry Andric           Diags.setDiagnosticGroupWarningAsError(Specifier, isPositive);
154*0b57cec5SDimitry Andric         } else if (DiagIDs->getDiagnosticsInGroup(Flavor, Specifier, _Diags)) {
155*0b57cec5SDimitry Andric           EmitUnknownDiagWarning(Diags, Flavor, "-Werror=", Specifier);
156*0b57cec5SDimitry Andric         }
157*0b57cec5SDimitry Andric         continue;
158*0b57cec5SDimitry Andric       }
159*0b57cec5SDimitry Andric 
160*0b57cec5SDimitry Andric       // -Wfatal-errors is yet another special case.
161*0b57cec5SDimitry Andric       if (Opt.startswith("fatal-errors")) {
162*0b57cec5SDimitry Andric         StringRef Specifier;
163*0b57cec5SDimitry Andric         if (Opt.size() != 12) {
164*0b57cec5SDimitry Andric           if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) {
165*0b57cec5SDimitry Andric             if (Report)
166*0b57cec5SDimitry Andric               Diags.Report(diag::warn_unknown_warning_specifier)
167*0b57cec5SDimitry Andric                 << "-Wfatal-errors" << ("-W" + OrigOpt.str());
168*0b57cec5SDimitry Andric             continue;
169*0b57cec5SDimitry Andric           }
170*0b57cec5SDimitry Andric           Specifier = Opt.substr(13);
171*0b57cec5SDimitry Andric         }
172*0b57cec5SDimitry Andric 
173*0b57cec5SDimitry Andric         if (Specifier.empty()) {
174*0b57cec5SDimitry Andric           if (SetDiagnostic)
175*0b57cec5SDimitry Andric             Diags.setErrorsAsFatal(isPositive);
176*0b57cec5SDimitry Andric           continue;
177*0b57cec5SDimitry Andric         }
178*0b57cec5SDimitry Andric 
179*0b57cec5SDimitry Andric         if (SetDiagnostic) {
180*0b57cec5SDimitry Andric           // Set the error as fatal flag for this specifier.
181*0b57cec5SDimitry Andric           Diags.setDiagnosticGroupErrorAsFatal(Specifier, isPositive);
182*0b57cec5SDimitry Andric         } else if (DiagIDs->getDiagnosticsInGroup(Flavor, Specifier, _Diags)) {
183*0b57cec5SDimitry Andric           EmitUnknownDiagWarning(Diags, Flavor, "-Wfatal-errors=", Specifier);
184*0b57cec5SDimitry Andric         }
185*0b57cec5SDimitry Andric         continue;
186*0b57cec5SDimitry Andric       }
187*0b57cec5SDimitry Andric 
188*0b57cec5SDimitry Andric       if (Report) {
189*0b57cec5SDimitry Andric         if (DiagIDs->getDiagnosticsInGroup(Flavor, Opt, _Diags))
190*0b57cec5SDimitry Andric           EmitUnknownDiagWarning(Diags, Flavor, isPositive ? "-W" : "-Wno-",
191*0b57cec5SDimitry Andric                                  Opt);
192*0b57cec5SDimitry Andric       } else {
193*0b57cec5SDimitry Andric         Diags.setSeverityForGroup(Flavor, Opt, Mapping);
194*0b57cec5SDimitry Andric       }
195*0b57cec5SDimitry Andric     }
196*0b57cec5SDimitry Andric 
197*0b57cec5SDimitry Andric     for (unsigned i = 0, e = Opts.Remarks.size(); i != e; ++i) {
198*0b57cec5SDimitry Andric       StringRef Opt = Opts.Remarks[i];
199*0b57cec5SDimitry Andric       const auto Flavor = diag::Flavor::Remark;
200*0b57cec5SDimitry Andric 
201*0b57cec5SDimitry Andric       // Check to see if this warning starts with "no-", if so, this is a
202*0b57cec5SDimitry Andric       // negative form of the option.
203*0b57cec5SDimitry Andric       bool IsPositive = !Opt.startswith("no-");
204*0b57cec5SDimitry Andric       if (!IsPositive) Opt = Opt.substr(3);
205*0b57cec5SDimitry Andric 
206*0b57cec5SDimitry Andric       auto Severity = IsPositive ? diag::Severity::Remark
207*0b57cec5SDimitry Andric                                  : diag::Severity::Ignored;
208*0b57cec5SDimitry Andric 
209*0b57cec5SDimitry Andric       // -Reverything sets the state of all remarks. Note that all remarks are
210*0b57cec5SDimitry Andric       // in remark groups, so we don't need a separate 'all remarks enabled'
211*0b57cec5SDimitry Andric       // flag.
212*0b57cec5SDimitry Andric       if (Opt == "everything") {
213*0b57cec5SDimitry Andric         if (SetDiagnostic)
214*0b57cec5SDimitry Andric           Diags.setSeverityForAll(Flavor, Severity);
215*0b57cec5SDimitry Andric         continue;
216*0b57cec5SDimitry Andric       }
217*0b57cec5SDimitry Andric 
218*0b57cec5SDimitry Andric       if (Report) {
219*0b57cec5SDimitry Andric         if (DiagIDs->getDiagnosticsInGroup(Flavor, Opt, _Diags))
220*0b57cec5SDimitry Andric           EmitUnknownDiagWarning(Diags, Flavor, IsPositive ? "-R" : "-Rno-",
221*0b57cec5SDimitry Andric                                  Opt);
222*0b57cec5SDimitry Andric       } else {
223*0b57cec5SDimitry Andric         Diags.setSeverityForGroup(Flavor, Opt,
224*0b57cec5SDimitry Andric                                   IsPositive ? diag::Severity::Remark
225*0b57cec5SDimitry Andric                                              : diag::Severity::Ignored);
226*0b57cec5SDimitry Andric       }
227*0b57cec5SDimitry Andric     }
228*0b57cec5SDimitry Andric   }
229*0b57cec5SDimitry Andric }
230